【问题标题】:How to split #__VA_ARGS__ in individual parameters如何在单个参数中拆分 #__VA_ARGS__
【发布时间】:2013-12-19 10:22:40
【问题描述】:

在可变参数宏中,#__VA_ARGS__ 是所有参数的逗号分隔字符串(至少我使用 gcc 和 clang 得到了这种行为)。有没有一种方法可以在不解析字符串的情况下为各个参数创建字符串数组?

我正在使用下面的代码来创建调试输出,例如

DEBUG DUMP int main() AT demo.cc:53:
        atan2(0.5, 0.5) => 0.785398
        1 + 2 => 3
        1 == 2 => false

从代码如

debug_dump(atan2(0.5, 0.5), 1 + 2, 1 == 2);

但我当前的解决方案使用临时词法分析器拆分 #__VA_ARGS__ 字符串,这当然无法解析具有模板参数的复杂案例,例如

debug_dump(std::pair<int,int>().first, 0 < 1);

因为没有简单的方法可以区分哪个&lt; 和/或&gt; 是模板参数的括号,哪个是移位或比较操作的一部分。这是我当前代码的一个简短的独立示例(需要 C++11):

#include <utility>
#include <stdio.h>
#include <math.h>

void debug_dump_val_worker(int v) { fprintf(stderr, "%d", v); }
void debug_dump_val_worker(bool v) { fprintf(stderr, "%s", v ? "true" : "false"); }
void debug_dump_val_worker(double v) { fprintf(stderr, "%f", v); }
void debug_dump_args_worker(const char *) { }

template <typename T, typename ... Args>
void debug_dump_args_worker(const char *p, T first, Args ... args)
{
        int next_p_state = 0;
        const char *next_p = p;
        while (*next_p && (next_p_state != 0 || *next_p != ',')) {
                if (*next_p == '"')
                        do {
                                next_p++;
                                while (*next_p == '\\' && *(next_p + 1))
                                        next_p += 2;
                        } while (*next_p && *next_p != '"');
                if (*next_p == '\'') {
                        next_p++;
                        if (*next_p == '\\')
                                next_p++;
                        if (*next_p)
                                next_p++;
                }
                if (*next_p == '(' || *next_p == '[' || *next_p == '{')
                        next_p_state++;
                if ((*next_p == ')' || *next_p == ']' || *next_p == '}') && next_p_state > 0)
                        next_p_state--;
                next_p++;
        }
        fprintf(stderr, "\n\t%.*s => ", int (next_p - p), p);
        if (*next_p == ',')
                next_p++;
        while (*next_p == ' ' || *next_p == '\t' || *next_p == '\r' || *next_p == '\n')
                next_p++;
        debug_dump_val_worker(first);
        debug_dump_args_worker(next_p, args ...);
}

#define debug_dump(...) do { \
        fprintf(stderr, "DEBUG DUMP %s AT %s:%d:", __PRETTY_FUNCTION__, __FILE__, __LINE__); \
        debug_dump_args_worker(#__VA_ARGS__, __VA_ARGS__); \
        fprintf(stderr, "\n"); \
} while (0)

int main()
{
        debug_dump(atan2(0.5, 0.5), 1 + 2, 1 == 2);
        debug_dump(std::pair<int,int>().first, 0 < 1);
        return 0;
}

【问题讨论】:

  • 就前任而言,std::pair&lt;int,int&gt; 是两个论点...
  • 感谢大家的快速回复。我将在debug_dump_args_worker(const char*) 中添加一个assert(*p == 0),以确保用户在这种情况下确实使用(...),正如@steve-jessop 所建议的那样。

标签: c++ c c++11 c-preprocessor


【解决方案1】:

正如 Yakk 所说,预处理器不会将 &lt;&gt; 视为匹配。

因此,如果您要这样做,那么您的代码需要比预处理器“更智能”。它需要完整的 C++ 编译器,一旦 __VA_ARGS__ 扩展为对 debug_dump_args_worker 的调用的参数,就可以将其全部整理并识别出有两个参数,而不是三个。在更复杂的情况下,&gt;&gt; 旨在关闭两个模板参数列表,直到 C++11 编译器才必须这样做,而不是将其视为异位移位运算符。所以实际上你的代码需要比 C++03 编译器“更智能”(也就是说,你需要上下文相关的标记化)。

我建议你最好的选择是放弃,并要求用户说:

debug_dump((std::pair<int,int>().first), 0 < 1);

相反。这是将模板参数放入单个宏参数的“通常”方式。您至少可以通过检查从字符串中提取的表达式数量是否等于参数包的长度来捕获错误。如果用户忘记在模板参数列表周围加上括号,那么您会发现字符串中的表达式太多。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-18
    • 1970-01-01
    • 2017-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多