Я хочу заставить препроцессор сделать для меня автоматическое создание кода. Мне не нужно много: простой цикл for, который содержит другой цикл for. [1]
Я читал все, что могу о расширении макросов, и больше не хихикаю, когда появляется синяя краска. В хороший день я могу даже объяснить, почему нужно многослойные макросы для генерации имени функции с помощью вставки меток. Я действительно получил работу for-loop. Но когда дело доходит до ввода цикла в цикл, я сводится к случайному разбрызгиванию с помощью DEFER, EVAL и OBSTRUCT и надеясь на лучшее.
Я не буду сдерживаться призывами к разуму. Я действительно хочу сделать это со стандартным препроцессором C. Я обещаю, что независимо от результата ни я, ни мой работодатель, ни мои наследники не будут судиться с вами за технологическую халатность. Я обещаю, что я не позволю кому-либо еще поддерживать код или даже просматривать его без соответствующих защитных очков. Притворяйся, если хочешь, я просто спрашиваю теоретический интерес. Или что мой единственный другой вариант использует M4: для, если рекурсивные макросы в CPP кажутся странными, конечно, M4 - это весь цыпленок.
Лучший справочный материал, который я нашел, - это 9-летняя тема Usenet: http://comp.std.c.narkive.com/5WbJfCof/double-cpp-expansion
Он начинается не по теме, немного мелочный и боевой по тону, и это путь над моей головой. Но я думаю, что ответ, который я ищу, где-то там.
Следующим лучшим является документация для заголовка злоупотребления CPP под названием Cloak: https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms
Это займет несколько иной подход к итерации и, возможно, будет служить моим потребностям. Но это также хороший обзор.
Вот несколько вырезанных кодов, чтобы показать, где я застрял.
repeat.h:
#define REPEAT(macro, times, start_n, next_func, next_arg, macro_args...) \
_REPEAT_ ## times(macro, start_n, next_func, next_arg, ## macro_args)
#define REPEAT_ADD_ONE(macro, times, start_n, macro_args... ) \
REPEAT(macro, times, start_n, _REPEAT_ADD_ONE, 0, ## macro_args)
#define _REPEAT_ADD_ONE(n, ignore...) _REPEAT_ADD_ONE_ ## n
#define _REPEAT_0(args...) /* empty */
#define _REPEAT_1(macro, n, func, i, args...) macro(n, ## args)
#define _REPEAT_2(m, n, f, i, a...) m(n, ## a); _REPEAT_1(m, f(n, i), f, i, ## a)
#define _REPEAT_3(m, n, f, i, a...) m(n, ## a); _REPEAT_2(m, f(n, i), f, i, ## a)
#define _REPEAT_4(m, n, f, i, a...) m(n, ## a); _REPEAT_3(m, f(n, i), f, i, ## a)
#define _REPEAT_5(m, n, f, i, a...) m(n, ## a); _REPEAT_4(m, f(n, i), f, i, ## a)
#define _REPEAT_6(m, n, f, i, a...) m(n, ## a); _REPEAT_5(m, f(n, i), f, i, ## a)
#define _REPEAT_7(m, n, f, i, a...) m(n, ## a); _REPEAT_6(m, f(n, i), f, i, ## a)
#define _REPEAT_8(m, n, f, i, a...) m(n, ## a); _REPEAT_7(m, f(n, i), f, i, ## a)
#define _REPEAT_9(m, n, f, i, a...) m(n, ## a); _REPEAT_8(m, f(n, i), f, i, ## a)
#define _REPEAT_10(m, n, f, i, a...) m(n, ## a); _REPEAT_9(m, f(n, i), f, i, ## a)
#define _REPEAT_ADD_ONE_0 1
#define _REPEAT_ADD_ONE_1 2
#define _REPEAT_ADD_ONE_2 3
#define _REPEAT_ADD_ONE_3 4
#define _REPEAT_ADD_ONE_4 5
#define _REPEAT_ADD_ONE_5 6
#define _REPEAT_ADD_ONE_6 7
#define _REPEAT_ADD_ONE_7 8
#define _REPEAT_ADD_ONE_8 9
#define _REPEAT_ADD_ONE_9 10
#define _REPEAT_ADD_ONE_10 11
#define _REPEAT_ADD_0(x) x
#define _REPEAT_ADD_1(x) _REPEAT_ADD_ONE(x)
#define _REPEAT_ADD_2(x) _REPEAT_ADD_1(_REPEAT_ADD_ONE(x))
#define _REPEAT_ADD_3(x) _REPEAT_ADD_2(_REPEAT_ADD_ONE(x))
#define _REPEAT_ADD_4(x) _REPEAT_ADD_3(_REPEAT_ADD_ONE(x))
#define _REPEAT_ADD_5(x) _REPEAT_ADD_4(_REPEAT_ADD_ONE(x))
#define _REPEAT_ADD_6(x) _REPEAT_ADD_5(_REPEAT_ADD_ONE(x))
#define _REPEAT_ADD_7(x) _REPEAT_ADD_6(_REPEAT_ADD_ONE(x))
#define _REPEAT_ADD_8(x) _REPEAT_ADD_7(_REPEAT_ADD_ONE(x))
#define _REPEAT_ADD_9(x) _REPEAT_ADD_8(_REPEAT_ADD_ONE(x))
#define _REPEAT_ADD_10(x) _REPEAT_ADD_9(_REPEAT_ADD_ONE(x))
sample.c:
#include "repeat.h"
#define INNER_MACRO(inner, outer) if (inner == outer) printf("Match\n")
#define INNER_BLOCK { if (inner == outer) printf("Match\n"); }
#define OUTER_MACRO_INNER_MACRO(outer) REPEAT_ADD_ONE(INNER_MACRO, 3, 0, outer)
#define OUTER_BLOCK_INNER_MACRO { REPEAT_ADD_ONE(INNER_MACRO, 3, 0, outer); }
#define OUTER_MACRO_INNER_BLOCK(outer) REPEAT_ADD_ONE(INNER_BLOCK, 3, 0, outer)
#define OUTER_BLOCK_INNER_BLOCK { REPEAT_ADD_ONE(INNER_BLOCK, 3, 0, outer); }
void outer_macro_inner_macro() {
REPEAT_ADD_ONE(OUTER_MACRO_INNER_MACRO, 2, 1);
}
void outer_macro_inner_block() {
REPEAT_ADD_ONE(OUTER_MACRO_INNER_BLOCK, 2, 1);
}
void outer_block_inner_macro() {
REPEAT_ADD_ONE(OUTER_BLOCK_INNER_MACRO, 2, 1);
}
void outer_block_inner_block() {
REPEAT_ADD_ONE(OUTER_BLOCK_INNER_BLOCK, 2, 1);
}
В sample.c
я показал четыре варианта, которые приближаются к тому, что я хочу. Но их нет. Вот что я получаю как вывод с помощью "cpp sample.c > out.c; astyle out.c;"
void outer_macro_inner_macro() {
REPEAT_ADD_ONE(INNER_MACRO, 3, 0, 1);
REPEAT_ADD_ONE(INNER_MACRO, 3, 0, 2);
}
void outer_macro_inner_block() {
REPEAT_ADD_ONE({ if (inner == outer) printf("Match\n"); }, 3, 0, 1);
REPEAT_ADD_ONE({ if (inner == outer) printf("Match\n"); }, 3, 0, 2);
}
void outer_block_inner_macro() {
{
if (0 == outer) printf("Match\n");
if (1 == outer) printf("Match\n");
if (2 == outer) printf("Match\n");
}(1);
{
if (0 == outer) printf("Match\n");
if (1 == outer) printf("Match\n");
if (2 == outer) printf("Match\n");
}(2);
}
void outer_block_inner_block() {
{ {
if (inner == outer) printf("Match\n");
}(0, outer);
{
if (inner == outer) printf("Match\n");
}(1, outer);
{
if (inner == outer) printf("Match\n");
}(2, outer);
}(1);
{ {
if (inner == outer) printf("Match\n");
}(0, outer);
{
if (inner == outer) printf("Match\n");
}(1, outer);
{
if (inner == outer) printf("Match\n");
}(2, outer);
}(2);
}
И вот результат, который я хочу получить вместо:
void desired_results() {
{
if (0 == 1) printf("Match\n");
if (1 == 1) printf("Match\n");
if (2 == 1) printf("Match\n");
};
{
if (0 == 2) printf("Match\n");
if (1 == 2) printf("Match\n");
if (2 == 2) printf("Match\n");
};
}
По сути, я могу заставить работать, если я использую блок как тело внешнего цикла, но не если я использую макрос функции. Но мне нужно использовать макрос с аргументами, чтобы тела цикла могли использовать счетчик циклов как константу, а не как переменную.
Проблема с макросом - макросом заключается в том, что внутренний рекурсивный вызов REPEAT_ADD_ONE() не расширяется. Ответ, казалось бы, отложил бы расширение внутреннего цикла до тех пор, пока не будет создан внешний цикл, а затем вытеснит другой проход, который расширяет внутренний цикл. Но по какой-то причине мой подход "случайной обезьяны" к этому еще не создал решение...
[1] Предполагаемое занижение.