字符串的递归预处理器宏

Recursive preprocessor macro for strings

本文关键字:处理器 预处理 递归 字符串      更新时间:2023-10-16

我有一些代码大部分需要 Unicode 字符串,但我想让它成为条件(即,TEXT("string")扩展到 L"string""string" ,具体取决于设置(。对于这些,我使用宏:

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

这样做的主要复杂性是 printf 格式字符串,它分别对相同编码和其他编码字符串使用 %s%S。一些字符串来自类似的条件 API(TCHAR 和类似(,而一些来自集合 API(主要是仅 C 字符串(。使用_tprintf和 family 时,所使用的函数可能会有所不同,使%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,但任何内容都值得关注。

递归宏扩展在 C/C++ 中是不可能的。

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

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

如果我错了,请纠正我。

上。

一个类似的 Unicode 相关问题:当存在修饰符(L、u8 等(时,相邻字符串文字连接会发生什么

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