递归预处理宏用于字符串

4

我有一些需要使用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-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,但任何内容都很有趣。


如果你使用 printf,我毫不同情。 - Puppy
@DeadMG,实际上我们花了一些时间来避免这种情况。不幸的是,接口会破坏任何模板化和大多数基于运算符的解决方案(如stringstream、boost.format、可变参数模板),或者将它们强制转换给调用者。在这种情况下,printf最终成为最优雅的解决方案(也许最好根本不记录日志)。:p - ssube
@DeadMG 如果你有任何避免这种情况的想法,我会非常乐意听取。 - ssube
2个回答

4

在C/C++中不可能进行递归宏展开。

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

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

如果我有错误,请纠正我。

更新。

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


看起来你是正确的,这会让事情变得足够简单。现在要找出编译器是否支持这个。 - ssube

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接