C-препроцессор: итеративно расширять макрос до списка, разделенного запятыми

Используя решение Пола Фульца II в сообщении рекурсивный макрос C-препроцессора, я хотел бы расширить неограниченное количество аргументов макроса в скобках, например

#define MY_CHAIN (alpha) (beta) (gamma)

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

CHAIN_COMMA(MY_CHAIN) // alpha, beta, gamma

Я могу раскрыться в фигурные скобки [alpha] [beta] [gamma] и разграничить список всем, что я пробовал, кроме запятой, alpha :: beta :: gamma в приведенном ниже примере.

Вот мой полный (компилирующий) код:

#include <iostream>
using namespace std;

// unrelated macro utilities
#define SEE(expression) cout << #expression ": " << STR(expression) << endl;
#define CMD(function, ...) function(__VA_ARGS__)
#define STR(s) CMD(STR_, s)
#define STR_(s) #s

// concatenation
#define CAT(x, y) CAT_(x, y)
#define CAT_(x,y) x ## y // error from CHAIN_COMMA: passed 4 arguments

// surround each chain element with square brackets []
#define CHAIN_BRACE(chain) CAT(CHAIN_BRACE_1 chain, _END)
#define CHAIN_BRACE_1(x) [x] CHAIN_BRACE_2
#define CHAIN_BRACE_2(x) [x] CHAIN_BRACE_1
#define CHAIN_BRACE_1_END
#define CHAIN_BRACE_2_END

// separate each chain element with the scope operator ::
#define CHAIN_SCOPE(chain) CAT(CHAIN_SCOPE_0 chain, _END)
#define CHAIN_SCOPE_0(x) x CHAIN_SCOPE_1
#define CHAIN_SCOPE_1(x) :: x CHAIN_SCOPE_2
#define CHAIN_SCOPE_2(x) :: x CHAIN_SCOPE_1
#define CHAIN_SCOPE_0_END
#define CHAIN_SCOPE_1_END
#define CHAIN_SCOPE_2_END

// trouble here: can't separate chain elements with commas
#define CHAIN_COMMA(chain) CAT(CHAIN_COMMA_0 chain, _END) // error
#define CHAIN_COMMA_0(x) x CHAIN_COMMA_1
#define CHAIN_COMMA_1(x) , x CHAIN_COMMA_2
#define CHAIN_COMMA_2(x) , x CHAIN_COMMA_1
#define CHAIN_COMMA_0_END
#define CHAIN_COMMA_1_END
#define CHAIN_COMMA_2_END

// define a custom chain and save various forms of it
#define MY_CHAIN (alpha) (beta) (gamma)
#define MY_BRACES CHAIN_BRACE(MY_CHAIN) // [alpha] [beta] [gamma]
#define MY_SCOPES CHAIN_SCOPE(MY_CHAIN) // alpha :: beta :: gamma
#define MY_COMMAS CHAIN_COMMA(MY_CHAIN) // alpha , beta , gamma

int main() {
    SEE(MY_CHAIN);
    SEE(MY_BRACES);
    SEE(MY_SCOPES);
//  SEE(MY_COMMAS); // error: macro "CAT_" passed 4 arguments, but takes just 2
    return 0;
}

Это выводит:

MY_CHAIN: (alpha) (beta) (gamma)
MY_BRACES: [alpha] [beta] [gamma]
MY_SCOPES: alpha :: beta :: gamma

Я попытался заключить в скобки список, разделенный запятыми, но CAT не добавит ) к _END. Любые умные идеи, чтобы расшириться в alpha, beta, gamma?


person Taylor Nichols    schedule 12.03.2018    source источник
comment
Некоторые возможные подсказки из stackoverflow.com/questions/13842468/comma-in-c-c-macro   -  person malat    schedule 12.03.2018


Ответы (1)


Поскольку запятая важна для вашего вывода и является синтаксическим элементом, вам нужно сделать запятую для вывода.

#define COMMA() ,

Нам также понадобятся некоторые отложенные функции, чтобы COMMA не оценивалось немедленно.

#define EMPTY()
#define DEFER(id) id EMPTY()

Теперь мы можем переопределить два ваших макроса в

#define CHAIN_COMMA_1(x) DEFER(COMMA)() x CHAIN_COMMA_2
#define CHAIN_COMMA_2(x) DEFER(COMMA)() x CHAIN_COMMA_1

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

Вы можете видеть, что макрос работает правильно, посмотрев на вывод препроцессора с параметром -E.

person unDeadHerbs    schedule 18.03.2018
comment
Спасибо! Хороший трюк с макросами DEFER/EMPTY, я его запомню. - person Taylor Nichols; 24.03.2018
comment
Большое спасибо. - person Joma; 15.05.2020