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
.
Я пропустил макрос цитаты из вопроса, потому что это незаконно в моем компиляторе.