Как разделить #__VA_ARGS__ по отдельным параметрам

0

Внутри вариационного макроса #__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__ с использованием ad-hoc lexer, что, конечно же, не позволяет анализировать сложные случаи с параметрами шаблона, такими как

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

потому что нет простого способа отличить, который < и/или > является скобкой для параметров шаблона и является частью операции сдвига или сравнения. Вот короткий автономный пример для моего текущего кода (требуется С++ 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;
}
  • 3
    Что касается препроцессора, std::pair<int,int> - это два аргумента ...
  • 0
    Спасибо за быстрый ответ всем. Я добавлю assert(*p == 0) в debug_dump_args_worker(const char*) чтобы удостовериться, что пользователь использовал (...) в таких случаях, как предложил @steve-jessop steve-jessop.
Теги:
c++11
c-preprocessor

1 ответ

2
Лучший ответ

То, что говорит Якк, препроцессор не рассматривает <> как согласованный.

Поэтому, если вы это сделаете, ваш код должен быть "умнее", чем препроцессор. Он принимает полный компилятор C++, как только __VA_ARGS__ будет расширен в аргументы вызова debug_dump_args_worker, чтобы разобраться во всем и признать, что есть два аргумента, а не три. И в более сложных случаях, когда >> предполагается закрыть два списка аргументов шаблона, до тех пор, пока C++ 11, что компиляторы не должны делать это, а не рассматривать его как оператор сдвига вне места. Поэтому на самом деле ваш код должен быть "умнее", чем компилятор C++ 03 (то есть вам нужна контекстно-зависимая токенизация).

Я предлагаю, чтобы ваш лучший вариант - сдаться и попросить пользователей сказать:

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

вместо. Это "обычный" способ получить аргументы шаблона в один аргумент макроса. Вы можете по крайней мере поймать ошибки, проверив, что количество выражений, которые вы извлекаете из строки, равно длине вашего пакета параметров. Если пользователь забывает помещать парсеры вокруг списка аргументов шаблона, то вы найдете слишком много выражений в строке.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню