【问题标题】:Is there a way to use C++ preprocessor stringification on variadic macro arguments?有没有办法对可变参数宏参数使用 C++ 预处理器字符串化?
【发布时间】:2011-08-22 21:29:31
【问题描述】:

我的猜测是这个问题的答案是否定的,但如果有办法,那就太棒了。为了澄清,假设我有以下宏:

#define MY_VARIADIC_MACRO(X...) // Does some stuff here in the macro definition

我想做的是在将 X 的所有变量传递给可变参数函数之前以某种方式对其进行字符串化;这里的关键字是之前。我意识到没有办法真正从宏定义中访问各个参数,但是有没有办法将所有参数字符串化,可能类似于以下内容?

#define MY_VARIADIC_MACRO(X...) some_variadic_function("some string", #X)

【问题讨论】:

    标签: c++ macros arguments variadic stringification


    【解决方案1】:

    这可能与您想要的不是很接近,但类似以下的内容可能会让您到达那里:

    #define MY_VARIADIC_MACRO(X) printf( "%s\n", #X )
    

    然后像下面这样使用它。将参数括在括号中,使其作为宏的一个参数出现。

    MY_VARIADIC_MACRO(( var1, var2, "string" ));

    然后你可以让宏调用一些函数来剥离外部括号或解析给定的字符串。

    【讨论】:

    • 差不多了,但想法是减轻括号括起来的需要...该死的 return 将注释添加到快速,需要记住 shift-return。我认为上述内容非常接近我正在努力的解决方案。我会投票赞成这个答案,因为它几乎就在那里,但不能将其标记为主要答案,因为它不完全满足要求。
    • @Zach:我也猜到了。我很想看看如何做到这一点。我的理解是这是不可能的,但我希望以不同的方式展示。
    • 我在上面发布了我将要使用的答案,对此我很兴奋,解决了我面临的很多问题。
    【解决方案2】:

    好的,我不是想在这里回答我自己的问题,但我想出了一个不错的解决方案,它在某种程度上结合了 Mark Wilkins 的回答和我在问题中给出的示例。

    可以对整个集合可变参数集进行字符串化,然后在字符串中包含分隔逗号。这是一个简单的例子:

    #define MY_VARIADIC_MACRO(X...) printf(#X)
    

    使用上面的宏向你展示了传递给宏的整个参数集都被字符串化了。

    然后您可以定义一个函数来使用分隔逗号标记这些参数,从而通过使用可变参数宏获取标记化字符串集:

    #define MY_VARIADIC_MACRO(X...) tokenize_my_arguments(#X)
    

    然后实际上不再依赖于让可变参数宏调用可变参数函数,我可以很好地遍历我的常量 C 字符串数组,而不是遍历 va_arg。

    * 来自 Edit Follows 的新内容 *

    根据蒂姆的评论,这是解决方案的详细信息。请原谅任何错误,因为它是仓促完成的,我不得不从我正在做的事情中移植。此外,它并不意味着复制/粘贴解决方案,因为它仅输出参数的字符串化以演示 POC,但应该足以演示功能。

    虽然这个解决方案需要一些运行时计算,但可变参数宏经常调用可变参数函数并需要遍历 va_args,因此在查找标记时会进行迭代,尽管可能会牺牲一些性能。然而,就可维护性、多功能性和易于实施而言,这似乎是目前最好的选择:

    #define VARIADIC_STRINGIFY(_ARGUMENTS_TO_STRINGIFY...) Variadic_Stringification_Without_Variadic_Function(#_ARGUMENTS_TO_STRINGIFY)
    
    void Variadic_Stringification_Without_Variadic_Function (const char* _stringified_arguments)
    {
        strcpy(converted_arguments, _stringified_arguments);
    
        for(char* token = strtok(converted_arguments, ","); token != 0x0; token = strtok(0x0, ","))
            std::cout << token << std::endl;
    }
    

    【讨论】:

    • 另外,它的另一个不错的功能是我可以轻松找到参数的数量,而无需通过宏传递某种类型的信息来确定列表中参数的结束位置。
    • 有趣,但在将其标记为解决方案之前,请发布解决此问题的实际代码。我相信它会对其他人派上用场。
    • @Tim 会的,我已经测试了一个简单的案例,并将使它成为一个更好的例子......有一些截止日期,明天(5/11/2011)或第二天发布.
    • @Tim 更新了详细信息。
    • +1。这似乎是一个很好的解决方案。我没有意识到 C99 支持可变参数宏。好的。我正在使用不支持它的 Microsoft 编译器(至少不支持这种形式)。
    【解决方案3】:

    您可以使用各种递归宏技术来处理可变参数宏。例如,您可以定义一个 NUM_ARGS 宏来计算可变参数宏的参数数量:

    #define _NUM_ARGS(X100, X99, X98, X97, X96, X95, X94, X93, X92, X91, X90, X89, X88, X87, X86, X85, X84, X83, X82, X81, X80, X79, X78, X77, X76, X75, X74, X73, X72, X71, X70, X69, X68, X67, X66, X65, X64, X63, X62, X61, X60, X59, X58, X57, X56, X55, X54, X53, X52, X51, X50, X49, X48, X47, X46, X45, X44, X43, X42, X41, X40, X39, X38, X37, X36, X35, X34, X33, X32, X31, X30, X29, X28, X27, X26, X25, X24, X23, X22, X21, X20, X19, X18, X17, X16, X15, X14, X13, X12, X11, X10, X9, X8, X7, X6, X5, X4, X3, X2, X1, N, ...)   N
    
    #define NUM_ARGS(...) _NUM_ARGS(__VA_ARGS__, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 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)
    

    然后,您可以编写一个 FOREACH 宏,为列表的每个元素扩展另一个宏:

    #define EXPAND(X)       X
    #define FIRSTARG(X, ...)    (X)
    #define RESTARGS(X, ...)    (__VA_ARGS__)
    #define FOREACH(MACRO, LIST)    FOREACH_(NUM_ARGS LIST, MACRO, LIST)
    #define FOREACH_(N, M, LIST)    FOREACH__(N, M, LIST)
    #define FOREACH__(N, M, LIST)   FOREACH_##N(M, LIST)
    #define FOREACH_1(M, LIST)  M LIST
    #define FOREACH_2(M, LIST)  EXPAND(M FIRSTARG LIST) FOREACH_1(M, RESTARGS LIST)
    #define FOREACH_3(M, LIST)  EXPAND(M FIRSTARG LIST) FOREACH_2(M, RESTARGS LIST)
            :
    

    这反过来又允许您定义将每个参数字符串化的宏:

    #define STRINGIFY(X)    #X
    #define MY_VARIADIC_MACRO(...)    FOREACH(STRINGIFY, (__VA_ARGS__))
    

    【讨论】:

    • 我知道那些递归技术,但是从上面可以看出它们很乏味,而且它们也有一个不幸的限制,即你被限制在编译时考虑的 args 数量。我将很快用该解决方案的详细信息更新我的答案,该解决方案实施起来并不繁琐,并且不限于更易于维护的预定义最大值。这可能是所有情况下的最佳解决方案,但对于我需要的是比上面更好的答案。感谢您的意见!
    • 第二个代码块末尾的冒号后面有原因吗?
    • 这是一个垂直省略号,省略了更多FOREACH_ 宏(可能高达FOREACH_100),您需要允许这么大的循环。
    猜你喜欢
    • 2016-05-08
    • 2014-09-01
    • 2017-02-25
    • 2013-03-14
    • 2020-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-16
    相关资源
    最近更新 更多