如何在各个参数中拆分#__VA_ARGS__

时间:2013-12-19 10:22:40

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

在可变参数宏中,#__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);

但是我当前的解决方案使用ad-hoc词法分析器拆分#__VA_ARGS__字符串,当然这无法用模板参数解析复杂的情况,例如

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

因为没有简单的方法来区分哪个<和/或>是模板参数的括号,哪个是转换或比较操作的一部分。这是我当前代码的一个简短的自包含示例(需要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;
}

1 个答案:

答案 0 :(得分:2)

Yakk说,预处理器不会将<>视为匹配。

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

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

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

代替。这是将模板参数转换为单个宏参数的“常用”方法。通过检查从字符串中提取的表达式的数量是否等于参数包的长度,您至少可以捕获错误。如果用户忘记在模板参数列表周围放置parens,那么你会在字符串中找到太多表达式。