C Макросы для создания строк

Альтернативные заголовки (для облегчения поиска)

  • Преобразование токена препроцессора в строку
  • Как сделать строку char из значения макроса C?

Оригинальный вопрос

Я хотел бы использовать C #define для построения литеральных строк во время компиляции.

Строка — это домены, которые меняются для отладки, выпуска и т. д.

Я хотел бы что-то вроде этого:

#ifdef __TESTING
    #define IV_DOMAIN domain.org            //in house testing
#elif __LIVE_TESTING
    #define IV_DOMAIN test.domain.com       //live testing servers
#else
    #define IV_DOMAIN domain.com            //production
#endif

// Sub-Domain
#define IV_SECURE "secure.IV_DOMAIN"             //secure.domain.org etc
#define IV_MOBILE "m.IV_DOMAIN"

Но препроцессор ничего не оценивает внутри

  1. Это можно обойти?
  2. Это вообще хорошая идея?

person Richard Stelling    schedule 28.04.2009    source источник
comment
Возможный дубликат преобразования токена препроцессора в строку   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 05.10.2015


Ответы (7)


В C строковые литералы объединяются автоматически. Например,

const char * s1 = "foo" "bar";
const char * s2 = "foobar";

s1 и s2 — это одна и та же строка.

Итак, для вашей проблемы ответ (без вставки токена)

#ifdef __TESTING
    #define IV_DOMAIN "domain.org"
#elif __LIVE_TESTING
    #define IV_DOMAIN "test.domain.com"
#else
    #define IV_DOMAIN "domain.com"
#endif

#define IV_SECURE "secure." IV_DOMAIN
#define IV_MOBILE "m." IV_DOMAIN
person Alex B    schedule 28.04.2009

Есть несколько способов сделать это:

  1. если вы имеете дело только со строковыми литералами, вы можете просто использовать простые строки use - размещение одного строкового литерала за другим заставляет компилятор объединять их.

  2. если могут быть задействованы другие вещи, кроме строковых литералов (т. е. вы создаете новые идентификаторы из макросов), используйте оператор вставки токена препроцессора «##». ваши макросы в буквальные строки.

Пример №1:

#ifdef __TESTING
    #define IV_DOMAIN "domain.org"                        //in house testing
#elif __LIVE_TESTING
    #define IV_DOMAIN "test.domain.com"           //live testing servers
#else
    #define IV_DOMAIN "domain.com"                        //production
#endif

// Sub-Domain
#define IV_SECURE "secure." IV_DOMAIN          //secure.domain.org etc
#define IV_MOBILE "m." IV_DOMAIN

И что касается оператора вставки токенов, я не думаю, что большинство ответов, в которых предлагалось использовать оператор препроцессора вставки токенов, действительно пробовали его - его может быть сложно использовать.

Использование часто предлагаемого ответа приведет к ошибке компилятора при попытке использовать макрос IV_SECURE, потому что:

#define IV_SECURE "secure."##IV_DOMAIN

расширяется до:

"secure"domain.org

Возможно, вы захотите попробовать использовать оператор '#`' 'stringing':

#define IV_SECURE "secure." #IV_DOMAIN

Но это не сработает, потому что работает только с аргументами макроса, а не только с любым старым макросом.

Одна вещь, о которой следует помнить, когда вы используете операторы препроцессора token-paste ('##') или строки ('#'), заключается в том, что вам нужно использовать дополнительный уровень косвенности, чтобы они работали правильно во всех случаях.

Если вы этого не сделаете и элементы, переданные оператору вставки токенов, сами по себе являются макросами, вы, вероятно, получите не то, что хотите:

#include <stdio.h>

#define STRINGIFY2( x) #x
#define STRINGIFY(x) STRINGIFY2(x)
#define PASTE2( a, b) a##b
#define PASTE( a, b) PASTE2( a, b)

#define BAD_PASTE(x,y) x##y
#define BAD_STRINGIFY(x) #x

#define SOME_MACRO function_name

int main() 
{
    printf( "buggy results:\n");
    printf( "%s\n", STRINGIFY( BAD_PASTE( SOME_MACRO, __LINE__)));
    printf( "%s\n", BAD_STRINGIFY( BAD_PASTE( SOME_MACRO, __LINE__)));
    printf( "%s\n", BAD_STRINGIFY( PASTE( SOME_MACRO, __LINE__)));

    printf( "\n" "desired result:\n");
    printf( "%s\n", STRINGIFY( PASTE( SOME_MACRO, __LINE__)));
}

Выход:

buggy results:
SOME_MACRO__LINE__
BAD_PASTE( SOME_MACRO, __LINE__)
PASTE( SOME_MACRO, __LINE__)

desired result:
function_name21

Таким образом, используя исходные определения IV_DOMAIN и служебные макросы, приведенные выше, вы можете сделать это, чтобы получить то, что хотите:

// Sub-Domain
#define IV_SECURE "secure." STRINGIFY( IV_DOMAIN)   //secure.domain.org etc
#define IV_MOBILE "m." STRINGIFY( IV_DOMAIN)
person Michael Burr    schedule 28.04.2009

Следующие строки объединяются компилятором C.

#define DOMAIN "example.com"
#define SUBDOMAIN "test." DOMAIN
const char *asCString = SUBDOMAIN;
NSString *asNSString = @SUBDOMAIN;
person rpetrich    schedule 28.04.2009

Я вижу много хороших и правильных ответов на ваш первый вопрос, но ни одного на ваш второй, так что вот что: я думаю, что это ужасная идея. Почему вы должны пересобирать свое программное обеспечение (особенно версию выпуска) только для того, чтобы изменить имя сервера? Кроме того, как вы узнаете, какая версия вашего программного обеспечения указывает на какой сервер? Вам нужно будет создать механизм для проверки во время выполнения. Если это вообще практично на вашей платформе, я рекомендую вам загрузить домены/URL-адреса из файла конфигурации. Только самая маленькая из встроенных платформ может быть «непрактичной» для этой цели :)

person rmeador    schedule 28.04.2009

Попробуйте использовать оператор ##

#define IV_SECURE secure.##IV_DOMAIN
person JaredPar    schedule 28.04.2009
comment
Я думаю, что ОП спрашивал о конкатенации строк (#), а не о вставке токенов (##). Однако я не минусовал. Вопрос немного двусмысленный. - person e.James; 28.04.2009
comment
Вставка токенов ('##') или создание строк ('#') не будут работать без дополнительных действий. - person Michael Burr; 28.04.2009

Вам нужны операторы # и ## и автоматическая конкатенация строк.

Оператор предварительной обработки # превращает параметр макроса в строку. Оператор ## вставляет вместе две лексемы (например, параметры макроса).

Возможность, которая приходит мне на ум,

#define IV_DOMAIN domain.org
#define IV_SECURE(DOMAIN) "secure." #DOMAIN

который должен изменить IV_SECURE на

#define IV_SECURE "secure." "domain.org"

который автоматически объединяется с «secure.domain.org» (при условии, что этапы перевода в C такие же, как и в C++).

ДРУГОЕ РЕДАКТИРОВАТЬ: Пожалуйста, прочитайте комментарии, которые показывают, как мне удалось запутаться. Имейте в виду, что я хорошо разбираюсь в C, хотя, возможно, немного заржавел. Я бы удалил этот ответ, но решил оставить его как пример того, как легко запутаться в препроцессоре C.

person David Thornley    schedule 28.04.2009
comment
## вставляет не строки, а токены. Тонкое, но важное отличие. - person Mark Ransom; 28.04.2009
comment
К сожалению, версия cpp, работающая в моем cygwin-боксе, не понимает оператора #, как вы его описываете. - person mouviciel; 28.04.2009
comment
Оператор # работает только с параметрами макроса. т.е. #define IV_SECURE(DOMAIN) безопасный. #DOMAIN В этом случае мало что поможет. - person Mark Ransom; 28.04.2009
comment
Эм, да. Я изменил это сейчас. Спасибо за исправления. - person David Thornley; 28.04.2009
comment
Есть еще проблема - IV_SECURE(IV_DOMAIN) выдает безопасный. IV_DOMAIN не защищен. домен.org - person Michael Burr; 28.04.2009
comment
@Michael: вы, вероятно, можете исправить это, используя два вложенных макроса. Прошло много времени с тех пор, как я делал что-то подобное, и мне пришлось бы изучить правила раскрытия макросов, прежде чем вспомнить, как это сделать. - person Mark Ransom; 28.04.2009
comment
Да, вам определенно нужно немного косвенности макросов. Вот почему использование операторов препроцессора ## и # сложнее, чем может показаться на первый взгляд. - person Michael Burr; 28.04.2009

Как уже отмечали другие, используйте вставку токенов. Вы также должны знать, что имена макросов, такие как

__TESTING

зарезервированы в C (не знаю о цели C) для реализации - вам не разрешено использовать их в своем собственном коде. Зарезервированные имена — это все, что содержит двойное подчеркивание и все, что начинается с подчеркивания и заглавной буквы.

person Community    schedule 28.04.2009
comment
Предполагается, что Objective C является строгим надмножеством C, и поэтому это должно быть действительным замечанием. - person Chris Lutz; 28.04.2009