Как написать цикл while с препроцессором C?

Я задаю этот вопрос с образовательной / хакерской точки зрения (я бы не хотел так писать код).

Можно ли реализовать цикл while только с помощью директив препроцессора C. Я понимаю, что макросы не могут быть расширены рекурсивно, так как это можно сделать?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
71
0
63 896
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

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

Оказывается, шаблоны C++ являются полными по Тьюрингу и могут использоваться аналогичным образом. Проверить Генеративное программирование

Вот нарушение правил, которое сделало бы это законным. Напишите свой собственный препроцессор C. Заставьте его интерпретировать некоторые директивы #pragma так, как вы хотите.

Это не ответ на вопрос, который явно говорит «Препроцессор то C», а не «Препроцессор а C». Очевидно, это относится к существующим цепочкам инструментов.

SOFe 24.07.2019 10:07
Ответ принят как подходящий

Взгляните на библиотеку Препроцессор Boost, которая позволяет писать циклы в препроцессоре и многое другое.

Для этой цели я использую мета-шаблонное программирование, это весело, когда вы освоите его. И иногда очень полезно, когда используется осмотрительно. Поскольку, как уже упоминалось, его тьюринг завершен, до такой степени, что вы даже можете заставить компилятор войти в бесконечный цикл или переполнение стека! Нет ничего лучше, чем пойти выпить кофе, просто чтобы обнаружить, что ваша компиляция использует более 30 гигабайт памяти и весь процессор для компиляции вашего кода бесконечного цикла!

Я нашел эту схему полезной, когда компилятор стал капризным и не разворачивал за меня определенные циклы.

#define REPEAT20(x) { x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;}

REPEAT20( val = pleaseconverge(val) );

Но ИМХО, если вам нужно что-то гораздо более сложное, то вы должны написать собственный препроцессор. Ваш препроцессор может, например, сгенерировать для вас соответствующий файл заголовка, и достаточно легко включить этот шаг в Makefile, чтобы все скомпилировалось без проблем с помощью одной команды. Я сделал это.

ну, не то чтобы это цикл while, а цикл счетчика, тем не менее цикл возможен в чистом CPP (без шаблонов и без C++)

#ifdef pad_always

#define pad(p,f) p##0

#else

#define pad0(p,not_used) p
#define pad1(p,not_used) p##0

#define pad(p,f) pad##f(p,)

#endif

// f - padding flag
// p - prefix so far
// a,b,c - digits
// x - action to invoke

#define n0(p,x)
#define n1(p,x)         x(p##1)
#define n2(p,x) n1(p,x) x(p##2)
#define n3(p,x) n2(p,x) x(p##3)
#define n4(p,x) n3(p,x) x(p##4)
#define n5(p,x) n4(p,x) x(p##5)
#define n6(p,x) n5(p,x) x(p##6)
#define n7(p,x) n6(p,x) x(p##7)
#define n8(p,x) n7(p,x) x(p##8)
#define n9(p,x) n8(p,x) x(p##9)

#define n00(f,p,a,x)                       n##a(pad(p,f),x)
#define n10(f,p,a,x) n00(f,p,9,x) x(p##10) n##a(p##1,x)
#define n20(f,p,a,x) n10(f,p,9,x) x(p##20) n##a(p##2,x)
#define n30(f,p,a,x) n20(f,p,9,x) x(p##30) n##a(p##3,x)
#define n40(f,p,a,x) n30(f,p,9,x) x(p##40) n##a(p##4,x)
#define n50(f,p,a,x) n40(f,p,9,x) x(p##50) n##a(p##5,x)
#define n60(f,p,a,x) n50(f,p,9,x) x(p##60) n##a(p##6,x)
#define n70(f,p,a,x) n60(f,p,9,x) x(p##70) n##a(p##7,x)
#define n80(f,p,a,x) n70(f,p,9,x) x(p##80) n##a(p##8,x)
#define n90(f,p,a,x) n80(f,p,9,x) x(p##90) n##a(p##9,x)

#define n000(f,p,a,b,x)                           n##a##0(f,pad(p,f),b,x)
#define n100(f,p,a,b,x) n000(f,p,9,9,x) x(p##100) n##a##0(1,p##1,b,x)
#define n200(f,p,a,b,x) n100(f,p,9,9,x) x(p##200) n##a##0(1,p##2,b,x)
#define n300(f,p,a,b,x) n200(f,p,9,9,x) x(p##300) n##a##0(1,p##3,b,x)
#define n400(f,p,a,b,x) n300(f,p,9,9,x) x(p##400) n##a##0(1,p##4,b,x)
#define n500(f,p,a,b,x) n400(f,p,9,9,x) x(p##500) n##a##0(1,p##5,b,x)
#define n600(f,p,a,b,x) n500(f,p,9,9,x) x(p##600) n##a##0(1,p##6,b,x)
#define n700(f,p,a,b,x) n600(f,p,9,9,x) x(p##700) n##a##0(1,p##7,b,x)
#define n800(f,p,a,b,x) n700(f,p,9,9,x) x(p##800) n##a##0(1,p##8,b,x)
#define n900(f,p,a,b,x) n800(f,p,9,9,x) x(p##900) n##a##0(1,p##9,b,x)

#define n0000(f,p,a,b,c,x)                               n##a##00(f,pad(p,f),b,c,x)
#define n1000(f,p,a,b,c,x) n0000(f,p,9,9,9,x) x(p##1000) n##a##00(1,p##1,b,c,x)
#define n2000(f,p,a,b,c,x) n1000(f,p,9,9,9,x) x(p##2000) n##a##00(1,p##2,b,c,x)
#define n3000(f,p,a,b,c,x) n2000(f,p,9,9,9,x) x(p##3000) n##a##00(1,p##3,b,c,x)
#define n4000(f,p,a,b,c,x) n3000(f,p,9,9,9,x) x(p##4000) n##a##00(1,p##4,b,c,x)
#define n5000(f,p,a,b,c,x) n4000(f,p,9,9,9,x) x(p##5000) n##a##00(1,p##5,b,c,x)
#define n6000(f,p,a,b,c,x) n5000(f,p,9,9,9,x) x(p##6000) n##a##00(1,p##6,b,c,x)
#define n7000(f,p,a,b,c,x) n6000(f,p,9,9,9,x) x(p##7000) n##a##00(1,p##7,b,c,x)
#define n8000(f,p,a,b,c,x) n7000(f,p,9,9,9,x) x(p##8000) n##a##00(1,p##8,b,c,x)
#define n9000(f,p,a,b,c,x) n8000(f,p,9,9,9,x) x(p##9000) n##a##00(1,p##9,b,c,x)

#define n00000(f,p,a,b,c,d,x)                                   n##a##000(f,pad(p,f),b,c,d,x)
#define n10000(f,p,a,b,c,d,x) n00000(f,p,9,9,9,9,x) x(p##10000) n##a##000(1,p##1,b,c,d,x)
#define n20000(f,p,a,b,c,d,x) n10000(f,p,9,9,9,9,x) x(p##20000) n##a##000(1,p##2,b,c,d,x)
#define n30000(f,p,a,b,c,d,x) n20000(f,p,9,9,9,9,x) x(p##30000) n##a##000(1,p##3,b,c,d,x)
#define n40000(f,p,a,b,c,d,x) n30000(f,p,9,9,9,9,x) x(p##40000) n##a##000(1,p##4,b,c,d,x)
#define n50000(f,p,a,b,c,d,x) n40000(f,p,9,9,9,9,x) x(p##50000) n##a##000(1,p##5,b,c,d,x)
#define n60000(f,p,a,b,c,d,x) n50000(f,p,9,9,9,9,x) x(p##60000) n##a##000(1,p##6,b,c,d,x)
#define n70000(f,p,a,b,c,d,x) n60000(f,p,9,9,9,9,x) x(p##70000) n##a##000(1,p##7,b,c,d,x)
#define n80000(f,p,a,b,c,d,x) n70000(f,p,9,9,9,9,x) x(p##80000) n##a##000(1,p##8,b,c,d,x)
#define n90000(f,p,a,b,c,d,x) n80000(f,p,9,9,9,9,x) x(p##90000) n##a##000(1,p##9,b,c,d,x)

#define cycle5(c1,c2,c3,c4,c5,x) n##c1##0000(0,,c2,c3,c4,c5,x)
#define cycle4(c1,c2,c3,c4,x) n##c1##000(0,,c2,c3,c4,x)
#define cycle3(c1,c2,c3,x) n##c1##00(0,,c2,c3,x)
#define cycle2(c1,c2,x) n##c1##0(0,,c2,x)
#define cycle1(c1,x) n##c1(,x)

#define concat(a,b,c) a##b##c

#define ck(arg) a[concat(,arg,-1)]++;
#define SIZEOF(x) (sizeof(x) / sizeof((x)[0]))

void check5(void)
{
    int i, a[32769];

    for (i = 0; i < SIZEOF(a); i++) a[i]=0;

    cycle5(3,2,7,6,9,ck);

    for (i = 0; i < SIZEOF(a); i++) if (a[i] != 1) printf("5: [%d] = %d\n", i+1, a[i]);
}

Если вы хотите реализовать цикл while, вам нужно будет использовать рекурсию в препроцессоре. Самый простой способ выполнить рекурсию - использовать отложенное выражение. Отложенное выражение - это выражение, для полного раскрытия которого требуется больше сканирований:

#define EMPTY()
#define DEFER(id) id EMPTY()
#define OBSTRUCT(id) id DEFER(EMPTY)()
#define EXPAND(...) __VA_ARGS__

#define A() 123
A() // Expands to 123
DEFER(A)() // Expands to A () because it requires one more scan to fully expand
EXPAND(DEFER(A)()) // Expands to 123, because the EXPAND macro forces another scan

Почему это важно? Когда макрос сканируется и расширяется, он создает контекст отключения. Этот контекст отключения приведет к тому, что токен, который относится к текущему расширяющемуся макросу, будет окрашен в синий цвет. Таким образом, если макрос будет окрашен в синий цвет, он больше не будет расширяться. Вот почему макросы не раскрываются рекурсивно. Однако контекст отключения существует только во время одного сканирования, поэтому, отложив раскрытие, мы можем предотвратить окрашивание наших макросов в синий цвет. Нам просто нужно будет применить больше сканирований к выражению. Мы можем сделать это с помощью этого макроса EVAL:

#define EVAL(...)  EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
#define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
#define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
#define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
#define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__)))
#define EVAL5(...) __VA_ARGS__

Затем мы определяем некоторые операторы для выполнения некоторой логики (например, if и т. д.):

#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__

#define CHECK_N(x, n, ...) n
#define CHECK(...) CHECK_N(__VA_ARGS__, 0,)

#define NOT(x) CHECK(PRIMITIVE_CAT(NOT_, x))
#define NOT_0 ~, 1,

#define COMPL(b) PRIMITIVE_CAT(COMPL_, b)
#define COMPL_0 1
#define COMPL_1 0

#define BOOL(x) COMPL(NOT(x))

#define IIF(c) PRIMITIVE_CAT(IIF_, c)
#define IIF_0(t, ...) __VA_ARGS__
#define IIF_1(t, ...) t

#define IF(c) IIF(BOOL(c))

Теперь со всеми этими макросами мы можем написать рекурсивный макрос WHILE. Мы используем макрос WHILE_INDIRECT для рекурсивного обращения к себе. Это предотвращает окрашивание макроса в синий цвет, поскольку он будет расширяться при другом сканировании (и с использованием другого контекста отключения). Макрос WHILE принимает макрос предиката, макрос оператора и состояние (которое является вариативными аргументами). Он продолжает применять этот макрос оператора к состоянию до тех пор, пока макрос предиката не вернет false (то есть 0).

#define WHILE(pred, op, ...) \
    IF(pred(__VA_ARGS__)) \
    ( \
        OBSTRUCT(WHILE_INDIRECT) () \
        ( \
            pred, op, op(__VA_ARGS__) \
        ), \
        __VA_ARGS__ \
    )
#define WHILE_INDIRECT() WHILE

В демонстрационных целях мы просто собираемся создать предикат, который проверяет, когда количество аргументов равно 1:

#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define NARGS(...) NARGS_SEQ(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)

#define IS_1(x) CHECK(PRIMITIVE_CAT(IS_1_, x))
#define IS_1_1 ~, 1,

#define PRED(x, ...) COMPL(IS_1(NARGS(__VA_ARGS__)))

Затем мы создаем оператор, который просто объединим два токена. Мы также создаем финальный оператор (называемый M), который будет обрабатывать окончательный вывод:

#define OP(x, y, ...) CAT(x, y), __VA_ARGS__ 
#define M(...) CAT(__VA_ARGS__)

Затем с помощью макроса WHILE:

M(EVAL(WHILE(PRED, OP, x, y, z))) //Expands to xyz

Конечно, ему можно передать любой предикат или оператор.

Не могли бы вы добавить немного пояснений к макросам? Это действительно интересно, но трудно получить. все эти перекрестные ссылки. Я не понимаю последней строчки. Что такое PRED, как он определен, взяв x, а здесь: M(EVAL(WHILE(**PRED**, OP, x, y, z))) //Expands to xyz, он ничего не принимает

dhein 18.12.2014 17:29

Еще несколько объяснений того, как все это работает, с дополнительными шагами между ними: здесь или здесь. Примеры в этих статьях не совсем такие же, как в этом ответе, но они достаточно близки.

Joris 16.06.2016 17:46

Прекрасный ответ - лишь бы он работал. Но вместо xyz он расширяется до WHILE ( PRED, OP, xy, z ), ср. coliru.stacked-crooked.com/a/1a7b4e7879bd7d4b

Armali 16.11.2017 15:41

@Armali Ой, произошла опечатка. Это должен быть OBSTRUCT(WHILE_INDIRECT), а не DEFER(WHILE_INDIRECT). См .: coliru.stacked-crooked.com/a/a381ddcbcae0b890

Paul Fultz II 17.11.2017 01:50

Другие вопросы по теме