C Макросы для создания строк - PullRequest
29 голосов
/ 28 апреля 2009

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

  • Преобразовать токен препроцессора в строку
  • Как сделать символьную строку из значения макроса 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. Это даже хорошая идея?

Ответы [ 7 ]

38 голосов
/ 28 апреля 2009

В 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
25 голосов
/ 28 апреля 2009

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

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

  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

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

#define IV_SECURE "secure." #IV_DOMAIN

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

При использовании операторов предварительной обработки token-paste ('##') или stringizing ('#') вы должны использовать дополнительный уровень косвенности, чтобы они правильно работали в *1032*. все дела.

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

#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)
8 голосов
/ 28 апреля 2009

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

#define DOMAIN "example.com"
#define SUBDOMAIN "test." DOMAIN
const char *asCString = SUBDOMAIN;
NSString *asNSString = @SUBDOMAIN;
7 голосов
/ 28 апреля 2009

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

6 голосов
/ 28 апреля 2009

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

#define IV_SECURE secure.##IV_DOMAIN
5 голосов
/ 28 апреля 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.

3 голосов
/ 28 апреля 2009

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

__TESTING

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

...