Рекурсивный макрос препроцессора для строк - PullRequest
4 голосов
/ 26 декабря 2011

У меня есть код, которому по большей части нужны строки Unicode, но я хочу сделать его условным (то есть, TEXT("string") расширяется до L"string" или "string", в зависимости от настроек). Для этого я использую макрос:

#ifdef _UNICODE
#    define VSTR(str) L##str
#else
#    define VSTR(str) str
#endif

Основным осложнением этого являются строки в формате printf, в которых используются %s и %S для строк с одинаковым и другим кодированием соответственно. Некоторые строки поступают из аналогичных условных API (TCHAR и аналогичных), в то время как другие поступают из set API (в основном только C-string). При использовании _tprintf и семейства используемые функции могут варьироваться, в результате чего %s и %S также будут условными, и их, возможно, придется перевернуть. Для этого я определил макросы для соответствующих элементов формата:

#ifdef _UNICODE
#    define VPFCSTR(str) "%S"
#    define VPFWSTR(str) "%s"
#    define VPFTSTR(str) VPFWSTR(str)
#else
#    define VPFCSTR(str) "%s"
#    define VPFWSTR(str) "%S"
#    define VPFTSTR(str) VPFCSTR(str)
#else

Теперь, все это работает нормально, но использует синтаксис:

VSTR("Beginning of a format string, with a string '") VPFTSTR VSTR("' included.")

Я бы хотел использовать такой синтаксис, как:

VSTR("Beginning of a format string, with a string '", VPFTSTR, "' included.")

Для Unicode это должно быть расширено до:

L"Beginning of a format string, with a string '" L"%s" L"' included."

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

Моей первой идеей было использовать __VA_ARGS__ для обработки этого, используя пустые аргументы, такие как:

VASTR(str, ...) VSTR(str) VASTR(__VA_ARGS__)

К сожалению, поскольку макросы не могут использоваться в их собственном определении, это терпит неудачу. Затем я попытался прокси:

VASTR2(...) VASTR(__VA_ARGS__)
VASTR(str, ...) VSTR(str) VASTR2(__VA_ARGS__)

Метод прокси тоже не работает.

Есть ли способ обработать запуск одного и того же макроса для каждого аргумента (другого) макроса, который принимает переменное число аргументов? Или, если нет, есть ли эквивалент? Если специфичен для компилятора, MSVC10 предпочтительнее, но все, что представляет интерес.

Ответы [ 2 ]

4 голосов
/ 26 декабря 2011

Рекурсивное расширение макросов невозможно в C / C ++.

Не уверен, но C ++ 0x позволяет опустить префикс кодирования для конкатенации строковых литералов. Таким образом, вы можете попытаться спроектировать ваш макрос, добавив L только к первому строковому литералу и использовать его следующим образом:

VSTR("Beginning of a format string, with a string '" VPFTSTR "' included.")

Пожалуйста, поправьте меня, если я ошибаюсь.

UPD.

Аналогичный вопрос, связанный с Unicode: Что происходит с конкатенацией литералов смежных строк при наличии модификатора (L, u8 и т. Д.)

1 голос
/ 16 января 2012

Используя библиотеку Boost.Preprocessor и эти дополнительные макросы, вы можете применить свой макрос VSTR к каждому аргументу:

//Your VSTR macro for one argument
#define VSTR_EACH(str) ...

/**
 * PP_NARGS returns the number of args in __VA_ARGS__
 */
#define PP_NARGS(...) \
         PP_DETAIL_NARG((__VA_ARGS__,PP_DETAIL_RSEQ_N()))
#define PP_DETAIL_NARG(args) \
         PP_DETAIL_ARG_N args
#define PP_DETAIL_ARG_N( \
          _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
         _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
         _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
         _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
         _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
         _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
         _61,_62,_63,N,...) N
#define PP_DETAIL_RSEQ_N() \
         63,62,61,60,                   \
         59,58,57,56,55,54,53,52,51,50, \
         49,48,47,46,45,44,43,42,41,40, \
         39,38,37,36,35,34,33,32,31,30, \
         29,28,27,26,25,24,23,22,21,20, \
         19,18,17,16,15,14,13,12,11,10, \
         9,8,7,6,5,4,3,2,1,0 

//Convert agruments list to a BOOST_PP_SEQ so we can iterate over it
//This is two macros in order to avoid the bug in MSVC compilers
#define DETAIL_PP_ARGS_TO_SEQ(size, tuple) BOOST_PP_TUPLE_TO_SEQ(size, tuple)
#define PP_ARGS_TO_SEQ(...) DETAIL_PP_ARGS_TO_SEQ(PP_NARGS(__VA_ARGS__), (__VA_ARGS__))

//The macro used inside of BOOST_PP_SEQ_FOR_EACH
#define VSTR_SEQ_EACH(t, data, x) VSTR_EACH(x)

#define VSTR(...) BOOST_PP_SEQ_FOR_EACH(VSTR_SEQ_EACH, ~, PP_ARGS_TO_SEQ(__VA_ARGS__))
...