C препроцессор, передающий несколько аргументов как один

Препроцессор C передает несколько аргументов, как если бы они были единственным аргументом. Я уверен, что проблема заключается в том, как мы вызываем макрос untouchable, но каждая попытка, которую мы сделали для изменения макроса first, не привела к желаемому результату. Вот полный образец кода с комментариями, которые объясняют, что происходит и что мы хотим:

//this can be changed, but it must remain a #define to be of any use to us
#define first a,b,c

// v all code below this line cannot be altered (it outside of our control)

#define untouchable(name, c1, c2, c3) \
    wchar_t name[] = \
    { \
        quote(c1), \
        quote(c2), \
        quote(c3) \
    }

#define quote(c) L#@c

// ^ all code above this line cannot be altered (it outside of our control)

int _tmain(int argc, _TCHAR* argv[])
{
    static untouchable(mrNess, first);

    // the line above is precompiled to:
    // static wchar_t mrNess[] = { L'a,b,c', L, L };

    // whereas we want:
    // static wchar_t mrNess[] = { L'a', L'b', L'c' };

    return 0;
}

Мы компилируем в Windows под VisualStudio.

Ответ 1

Когда препроцессор сталкивается с вызовом функционально-подобного макроса, он сначала идентифицирует аргументы, а затем полностью макро-расширяет их * а последний заменяет вызов макроса его списком замещения, расширенные аргументы, вставленные в соответствующие места. Расширение макроса first выполняется (или будет) слишком поздно для препроцессора для распознавания в разложении отдельных аргументов макроса untouchable(). Вам нужно будет найти другой способ.

Одна возможность заключается в том, чтобы вставить уровень косвенности:

#define untouch_wrap(name, args) untouchable(name, args)

static untouch_wrap(mrNess, first);

Там first расширяется до untouch_wrap, поэтому, когда результат повторно сканируется для замены последующих макросов, в результате вызова untouchable() имеет правильное количество аргументов.

Это означает, что второй аргумент untouch_wrap() будет расширяться до списка, разделенного запятыми, ровно из трех членов. Было бы более чистым и гибким определить untouch_wrap() следующим образом:

#define untouch_wrap(name, ...) untouchable(name, __VA_ARGS__)

... но обработка переменных переменных в MSVC является несоответствующей, и я подозреваю, что она преодолеет это.


* Расширение подавляется для аргументов, которые являются операндами операторов препроцессора (#) и token-pasting (##).

Ответ 2

Проблема заключается в том, как вы определяете first. Вы передаете first в макрос untouchable, но в то время он явно не расширяется.

Поэтому не используйте макрос first и передайте a, b и c в качестве отдельных аргументов макроса. Тогда вы получите то, что хотите.

Update

Но есть надежда:

#define UT_HELPER(a,b,c,d) untouchable(a,b,c,d)
#define UT(x, y) UT_HELPER(x, y)
#define first a, b, c
static UT(mrNess, first);

Это похоже на решение, найденное в этом ответе, поэтому я не могу утверждать, что сам это придумал.

Обратите внимание, что

#define quote(s) L#@s

не работает в моем компиляторе, поэтому я не могу полностью его протестировать. Я просто пробовал с помощью

#define quote(s) L###s

и это сработало.

Надеюсь, это заставит вас пойти на реальные, более сложные макросы, которые вы, кажется, имеете.

Ответ 3

C11, 6.10.3.1 Аргумент Замена:

После того, как аргументы для вызова функционально-подобного макроса были идентифицированы, замена аргументов имеет место. Параметр в список заметок, за исключением случаев, когда предшествует токен # или ## предварительной обработки или за которым следует токен ## предварительной обработки (см. ниже), заменяется на соответствующий аргумент после того, как все макросы, содержащиеся в нем, были расширен. Перед заменой каждая предварительная обработка аргументов жетоны полностью заменены макросами, как если бы они составляли остальную часть файл предварительной обработки; нет других токенов предварительной обработки.

Посмотрите, как это расширено:

#define first a,b,c
#define untouchable(name, c1, c2, c3) \
    wchar_t name[] = \
    { \
        quote(c1), \
        quote(c2), \
        quote(c3) \
    }

untouchable(foo,first,c,d)

в

$ gcc -E foo.c
...

wchar_t foo[] = { quote(a,b,c), quote(c), quote(d) }  

Сначала аргументы идентифицируются, поэтому неприкасаемый параметр сначала становится c1. Только после того, как они идентифицированы, они расширяются, а затем заменяются на макрообъект. first -> c1 -> a,b,c. Теперь в корпусе макроса c1 заменяется на a,b,c.

Я пропустил макрос цитаты из вопроса, потому что это незаконно в моем компиляторе.