Генерация списков инициализаторов с использованием препроцессора C - PullRequest
0 голосов
/ 19 июня 2020

Мне было интересно, можно ли создать макрос C99, который может использовать этот синтаксис

MAGIC(a,b,(c,d),(e,(f,g))) // Expands to {{a}, {b}, {{c, d}}, {{e, {f,g}}}}

Или этот более функциональный синтаксис

MAGIC( (a)(b)(c,d)(e,(f,g)) ) // Again should expands to {{a}, {b}, {{c, d}}, {{e, {f,g}}}}

Если нужно, я могу Предположим, что максимальная глубина вложения в скобках равна, скажем, четырем уровням.

Я поигрался с представленным решением здесь . Но пока я далеко не ушел. Я пытаюсь предотвратить создание этого поколения, например макроса, для имитации рекурсии / итерации. Но обойти это, наверное, не удастся.

1 Ответ

1 голос
/ 20 июня 2020

Частично то, что усложняет препроцессор C, заключается в том, что он требует рекурсии в группировки в скобках. На практике это означает, что либо нам нужно применять задержки / оценки на внешних уровнях, либо нам нужны разные макросы на внутренних. Также существует асимметрия в спецификации на уровне рекурсии, которая, хотя и не сложна сама по себе, должна быть учтена.

Если необходимо, я могу предположить максимальную глубину вложенности в скобках, например четыре уровня.

Я согласен на это ... и поддержу четыре уровня.

Определения

Определения (с использованием терминов, совместимых с препроцессором ускорения): A кортеж - это структура данных предварительной обработки, состоящая из одной группы в скобках, элементы которой разделены запятыми. Итак, (a,(b,c),d) - это кортеж с тремя элементами ... a, (b,c) и d. Последовательность - это структура данных предварительной обработки, состоящая из серии смежных элементов, каждый из которых заключен в группу в скобках. Таким образом, те же элементы, перечисленные выше, представлены последовательностью (a)((b,c))(d).

Дизайн

Для обработки кортежей с использованием вариативности нам нужен счетчик, связующий макрос и макрос для каждого из размеры кортежей, которые мы хотим поддерживать. Если, скажем, мы поддерживаем кортежи от 1 до 10 элементов и хотим изменить его для поддержки кортежей от 1 до 20, нам, возможно, придется изменить счетчик и добавить еще 10 макросов. Но если мы поддерживаем 4 уровня рекурсии с использованием одних только кортежей, нам потребуется в 4 раза больше макросов; это делает масштабирование более обременительным.

Вы показываете две формы макросов; оба используют кортежи, последний просто использует последовательность на верхнем уровне. Для единообразия я выберу первую форму. Но для масштабируемости я добавлю обработку кортежей только для преобразования кортежа в последовательность, показывая поддержку кортежей из 9 элементов ... затем, чтобы поддерживать кортежи из n элементов, вам нужно только изменить счетчик и добавить макросы n-9.

Подход

Мы начинаем с базового c связующего макроса, счетчика и преобразователя последовательности на одном уровне (PARN):

#define GLUE(A,B) GLUE_I(A,B)
#define GLUE_I(A,B) A##B
#define PARN(...) GLUE(PARN_, COUNT (__VA_ARGS__)) (__VA_ARGS__)
#define PARN_9(A,B,C,D,E,F,G,H,I) (A)(B)(C)(D)(E)(F)(G)(H)(I)
...
#define PARN_2(A,B) (A)(B)
#define PARN_1(A) (A)

Мы хотите либо перебрать другой уровень, либо просто обработать элемент в зависимости от того, заключен ли он в скобки. Для этого я буду применять сопоставление с образцом на основе «косвенного второго макроса». Идея состоит в том, что вы помещаете шаблон в аргумент 1 и настраиваете макросы таким образом, чтобы, если шаблон совпадает, он сдвигал токены в аргумент 2; в противном случае ваш шаблон просто создает кучу токенов в аргументе 1, которые игнорируются. Вот конструкция сопоставления шаблонов с детектором в скобках (обратите внимание, что мой второй расширяется до пустого, если вы передаете ему 1 аргумент):

#define SECOND(...) SECOND_I(__VA_ARGS__,,)
#define SECOND_I(A,B,...) B
#define CALLDETECT(...) ,

Учитывая предоставленную вами ссылку, вы уже должны знать, как применять последовательности. У меня есть специальный макрос c для приложения последовательности:

#define PASTE_E(...) PASTE_E_I(__VA_ARGS__)
#define PASTE_E_I(...) __VA_ARGS__ ## E

... типичная «простая» обработка последовательности переключается между двумя макросами, называйте их A и B, пока они не попадут в терминал ... поэтому они go A, B, A, B, ..., E. Но мы хотим оставить след в виде результатов, разделенных запятыми, без лишних запятых. Итак, мы добавим запятую в каждый из этих макросов, но начнем с другого A, которое не добавляется; т.е. мы идем к go A, B, C, B, C, ..., E. Нам понадобятся четыре из этих наборов, чтобы (а) избежать синей краски и (б) обработать иначе на верхнем уровне. Вот начальный набор:

#define BRACP0(X) {SECOND(CALLDETECT X MAGIC1)X}
#define BRAC0_A(X) BRACP0(X)BRAC0_B
#define BRAC0_B(X) ,BRACP0(X)BRAC0_C
#define BRAC0_C(X) ,BRACP0(X)BRAC0_B
#define BRAC0_BE
#define BRAC0_CE

Это привяжет внешний уровень MAGIC к внутреннему уровню MAGIC1; этот набор специально добавляет дополнительную {} пару на SP c (BRACP0). В остальном наборы похожи, за исключением {}. Но для последнего уровня мы хотим вызвать простую bracify вместо «next magi c» (это тоже масштабируемое; вам нужен один из этих наборов для каждой глубины рекурсии уровня). Bracify и BRACP уровня 4 выглядят так:

#define BRACIFY(...) {__VA_ARGS__}
#define BRACP4(X) SECOND(CALLDETECT X BRACIFY)X

У меня есть «развертка» для каждого мага c макроуровень:

#define MAGIC(...) {PASTE_E(MAGIC_U(BRAC0_A PARN(__VA_ARGS__)))}
#define MAGIC_U(...) __VA_ARGS__
#define MAGIC1(...) {PASTE_E(MAGIC1_U(BRAC1_A PARN(__VA_ARGS__)))}
#define MAGIC1_U(...) __VA_ARGS__
...
#define MAGIC4(...) {PASTE_E(MAGIC4_U(BRAC3_A PARN(__VA_ARGS__)))}
#define MAGIC4_U(...) __VA_ARGS__

Полное решение и демонстрация

Ссылка на coliru.stacked-crooked demo.

Заключительные комментарии

Я интерпретировал здесь C99 как имеющий значение стандарт, а этот вопрос - как просто «возможно ли»; поэтому я не стал преобразовывать это для работы с препроцессором MSVS. Вероятно, не будет работать с как есть. Сообщите мне, если это вас вообще беспокоит.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...