字符串的递归预处理器宏

时间:2011-12-26 00:45:19

标签: c++ c c-preprocessor

我有一些代码需要大部分的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__)

代理方法似乎也不起作用。

有没有办法处理在(nother)宏的每个参数上运行相同的宏,这需要可变数量的参数?或者,如果没有,是否有相同的?如果是编译器特定的,MSVC10是首选,但任何东西都是有意义的。

2 个答案:

答案 0 :(得分:4)

C / C ++中无法进行递归宏扩展。

不确定,但C ++ 0x允许您省略字符串文字串联的编码前缀。因此,您可以尝试设计宏以仅将L添加到第一个字符串文字并按如下方式使用它:

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

如果我错了,请纠正我。

UPD。

类似的Unicode相关问题:What happens with adjacent string literal concatenation when there is a modifier(L, u8, etc.)

答案 1 :(得分:1)

使用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__))