Как использовать список макросов дважды в одном «стеке вызовов»? - PullRequest
0 голосов
/ 04 марта 2019

У меня есть информация, хранящаяся в списке макросов, например:

#define MYLIST(XX) \
  XX(1, hello)     \
  XX(2, world)     \
  ...

Теперь я хотел бы использовать этот макрос дважды в одном и том же «стеке вызовов».Вот глупый пример:

#define BB(i,n) i +
#define AA(i,n) i + (MYLIST(BB) 0) +
int foo = MYLIST(AA) 0;

Однако это не работает, поскольку MYLIST не расширяется во второй раз:

int foo = 1 + (MYLIST(BB) 0) + 2 + (MYLIST(BB) 0) + 0;

Есть лиспособ использовать MYLIST в одном и том же «стеке вызовов» дважды - или обходной путь - используя мой существующий список?

1 Ответ

0 голосов
/ 04 марта 2019

Будет работать следующий код:

#define EVAL(...) __VA_ARGS__
#define EVAL2(...) EVAL(__VA_ARGS__)
#define EMPTY()
#define DELAYED_CALL(F, ...) F EMPTY()(__VA_ARGS__)

#define BB(i,n) i +
#define AA(i,n) i + (MYLIST(BB) 0) +

#define MYLIST(XX) \
    DELAYED_CALL(XX, 1, hello) \
    DELAYED_CALL(XX, 2, world)

int foo = EVAL2(MYLIST(AA)) 0;

Вывод: int foo = 1 + (1 + 2 + 0) + 2 + (1 + 2 + 0) + 0;

К сожалению, у меня нет глубокого понимания , почему это работает;Я только что попробовал несколько трюков, которые, как правило, помогают в подобных случаях.Но я могу объяснить кое-что из этого.

Иногда макрос помечается как «не подлежащий дальнейшему расширению».Флаг обычно устанавливается, когда вы начинаете расширять его, и сбрасывается, когда расширение заканчивается.Это имеет тенденцию предотвращать рекурсию.

Когда макрос раскрывается в токены, которые обычно были бы функциональными вызовами макросов, иногда мы уже прошли этап, на котором они были бы расширены.

Мыможет обойти первую проблему, отложив развертывание макроса до точки, где флаг не вызовет никаких проблем, добавив второй макрос в create вызов макроса при его оценке.Вот что делает DELAYED_CALL.Но при этом мы сталкиваемся со второй проблемой, поэтому нам нужно добавить несколько вызовов к EVAL, чтобы вызвать повторное сканирование макроса (аргументы к функционально-подобному макросу всегда сканируются, поэтому передается последовательность токеновфункционально-подобный макрос, который просто выводит свои аргументы, вызывает повторное сканирование).

Иногда нам требуется пара повторных проверок, чтобы все работало.EVAL2(X) это просто сокращение для EVAL(EVAL(X)).Иногда потребуется больше уловок.

Приведенный ниже код немного проясняет происходящее.Обратите внимание, что версии MYLIST2 требуется на один меньше EVAL;это связано с тем, что AA вызывает MYLIST, а MYLIST2 имеет установленный флаг сбоя.

#define EVAL(...) __VA_ARGS__
#define EVAL2(...) EVAL(__VA_ARGS__)
#define EVAL3(...) EVAL2(__VA_ARGS__)
#define EMPTY()
#define DELAYED_CALL(F, ...) F EMPTY()(__VA_ARGS__)

#define BB(i,n) i +
#define AA(i,n) i + (MYLIST(BB) 0) +

#define MYLIST(XX) \
    DELAYED_CALL(XX, 1, hello) \
    DELAYED_CALL(XX, 2, world)

#define MYLIST2(XX) \
    XX(1, hello) \
    XX(2, world)

% MYLIST
int foo = MYLIST(AA) 0;
int foo = EVAL(MYLIST(AA)) 0;
int foo = EVAL2(MYLIST(AA)) 0;

% MYLIST2
int foo = MYLIST2(AA) 0;
int foo = EVAL(MYLIST2(AA)) 0;
int foo = EVAL2(MYLIST2(AA)) 0;

Вывод этого будет:

% MYLIST
int foo = AA (1, hello) AA (2, world) 0;
int foo = 1 + (BB (1, hello) BB (2, world) 0) + 2 + (BB (1, hello) BB (2, world) 0) + 0;
int foo = 1 + (1 + 2 + 0) + 2 + (1 + 2 + 0) + 0;

% MYLIST2
int foo = 1 + (BB (1, hello) BB (2, world) 0) + 2 + (BB (1, hello) BB (2, world) 0) + 0;
int foo = 1 + (1 + 2 + 0) + 2 + (1 + 2 + 0) + 0;
int foo = 1 + (1 + 2 + 0) + 2 + (1 + 2 + 0) + 0;

(Знаки% aren 'Ничего особенного. Я просто хотел, чтобы комментарии выводились в выводе, а комментарии в стиле C удалялись во время предварительной обработки.)

Дальнейшее чтение .Автор статьи понимает это гораздо лучше, чем я.

...