Есть ли способ выполнить произвольный код в препроцессоре C? - PullRequest
0 голосов
/ 05 февраля 2019

Рассмотрим эту программу:

#define MACRO                                   \
    srand(time(NULL));                          \
    if (rand() % 2)                             \
        printf("A");                            \
    else                                        \
        printf("B");

int main() {
    MACRO;
}

Это эквивалент следующей макро-расширенной программы:

int main() {
    srand(time(NULL));
    if (rand() % 2)
        printf("A");
    else
        printf("B");
}

Очевидно, что оператор if выполняется во время выполнения, а нев фазе макроразложения.Если оператор if был выполнен во время компиляции, и если было синтаксическое цитирование (как в Lisp), мы могли бы получить программы A и A 'с (почти) равной вероятностью.Где A:

int main() {
    printf("A");
}

и A ':

int main() {
    printf("B");
}

Есть ли способ заставить GCC излучать A или A' в отношении условия, оцениваемого во время компиляции?

Ответы [ 3 ]

0 голосов
/ 05 февраля 2019

Есть ли способ выполнить произвольный код в препроцессоре C?

Нет, и это было преднамеренное дизайнерское решение авторов оригинального (1989 г.) стандарта C.Они были знакомы с гораздо более мощными макрос-системами (например, из Lisp, который допускает произвольные вычисления во время компиляции), и они думали, что они слишком затрудняют понимание того, что означает незнакомая программа.

Это не яснодля меня, почему вы хотите сделать случайный выбор во время компиляции.Если вы объясните свои более крупные цели, мы сможем быть более полезными.

0 голосов
/ 06 февраля 2019

Для компиляции на основе результата модуля с двумя случайными числами нам нужно будет использовать BoostPP .

#include <boost/preprocessor/control/if.hpp>
#include <boost/preprocessor/arithmetic/mod.hpp>

BOOST_PP_IF(BOOST_PP_MOD(RAND, 2), printf("A"); printf("B");)

И вам нужно будет скомпилировать, используя-D опция для присвоения RAND из /dev/random, как предложено вам в комментарии.

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

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

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

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

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

#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))

#define EAT(...)
#define EXPAND(...) __VA_ARGS__
#define WHEN(c) IF(c)(EXPAND, EAT)

И использование.Обратите внимание, что WHEN(0) не расширяется, и любой другой WHEN(arg) будет расширяться.

WHEN(0) ( \
  printf("A"); \
)
WHEN(1) ( \
  printf("B"); \
)
WHEN(whatever) ( \
  printf("this will expand too"); \
)

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

0 голосов
/ 05 февраля 2019

Вы не можете выполнить код в препроцессоре.Однако вы можете создать код, который генерирует код:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() 
{
    printf("#include <stdio.h>\n");
    printf("\n");
    printf("int main()\n");
    printf("{\n");

    srand(time(NULL));
    if (rand() % 2)
        printf("  printf(\"A\");\n");
    else
        printf("  printf(\"B\");\n");

    printf("}\n");
}

Вывод:

#include <stdio.h>

int main()
{
  printf("A");
}

Или:

#include <stdio.h>

int main()
{
  printf("B");
}
...