我有一些代码需要大部分的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是首选,但任何东西都是有意义的。
答案 0 :(得分:4)
C / C ++中无法进行递归宏扩展。
不确定,但C ++ 0x允许您省略字符串文字串联的编码前缀。因此,您可以尝试设计宏以仅将L
添加到第一个字符串文字并按如下方式使用它:
VSTR("Beginning of a format string, with a string '" VPFTSTR "' included.")
如果我错了,请纠正我。
类似的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__))