Как расширить несколько макросов на основе вариационного макроса - PullRequest
1 голос
/ 05 мая 2019

У меня есть много макросов, которые в конечном итоге генерируют код.Например:

#define CODE_GEN_IDENT1_HDR(PARAM1, PARAM2, PARAM3) \
   // code generated

#define CODE_GEN_IDENT2_HDR(PARAM1, PARAM2, PARAM3) \
   // code generated

#define CODE_GEN_IDENT1_SRC(PARAM1, PARAM2, PARAM3) \
    // code generated

#define CODE_GEN_IDENT2_SRC(PARAM1, PARAM2, PARAM3) \
    // code generated

Идея состоит в том, что HDR генерирует определения функций, а SRC генерирует их реализацию.Все макросы имеют одинаковое количество аргументов (в данном примере 3).IDENT может быть любым именем, например MATH, TRIG, ALGS, CONTAINERS и т. Д. Это то, на чем я хочу сосредоточиться.

Я пытаюсь создать макроскоторый может генерировать все эти макросы макросов с различными IDENT, используя переменные макросы.Например:

// Concatenate macros to a standard form
#define CONCATH_(C) CODE_GEN_##C##_HDR
#define CONCATC_(C) CODE_GEN_##C##_SRC

// CONCATH concatenates to HDR
// CONCATC concatenates to SRC
#define CONCATH(C) CONCATH_(C)
#define CONCATC(C) CONCATC_(C)

#define MASTER_MACRO(PARAM1, PARAM2, PARAM3, ...) \
    // Code that generates all other macros
    // using CONCATH and CONCATC
    // how could this be done?

Когда я пишу:

MASTER_MACRO(int, "Hello", char *, MATH, TRIG, CONT)

Я хотел бы иметь что-то вроде:

CODE_GEN_MATH_HDR(int, "Hello", char *)
CODE_GEN_TRIG_HDR(int, "Hello", char *)
CODE_GEN_CONT_HDR(int, "Hello", char *)
CODE_GEN_MATH_SRC(int, "Hello", char *)
CODE_GEN_TRIG_SRC(int, "Hello", char *)
CODE_GEN_CONT_SRC(int, "Hello", char *)

Чтобы как-то получить доступ к заданным аргументам и объединитькаждый, создавая и заголовок, и источник.

В настоящее время у меня есть макрос фиксированной длины, например:

MASTER_MACRO(PARAM1, PARAM2, PARAM3, MATH, TRIG, CONT, DUPL, SORT) \
    CONCATH(MATH)(PARAM1, PARAM2, PARAM3)
    CONCATH(TRIG)(PARAM1, PARAM2, PARAM3)
    ...
    CONCATC(MATH)(PARAM1, PARAM2, PARAM3)
    ...

А когда пользователь не хочет генерировать CONT, DUPL или любой другой макрос, который он должен передать в предопределенном аргументе, например _, что означает, что он не хочет генерировать это и имеет пустой макрос:

#define CODE_GEN___SRC(PARAM1, PARAM2, PARAM3) // Empty

Но этого недостаточно.В разных проектах эти разные IDENT имеют разные имена, поэтому необходимость создания новых основных макросов немного раздражает.

Но большие вопросы:

  • Можно ли это сделать?
  • Есть ли простой способ сделать это?
  • Можно ли это сделать, используя только стандартный C (без макроса, зависящего от компилятора)?

1 Ответ

1 голос
/ 05 мая 2019

Да, вы можете сделать это.Для реализации, созданной вручную, вы, вероятно, захотите начать с базового счетчика аргументов, например:

#define COUNT(...) \
        COUNT_I(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1,)
#define COUNT_I(_          ,_9,_8,_7,_6,_5,_4,_3,_2, X,...) X

Счетчик аргументов работает как «сдвиговый регистр», вставляя список аргументов перед счетчиком.Если вызывается с одним аргументом, все выравнивает X с 1. Каждый дополнительный аргумент сдвигает этот список по ... 2 аргумента сдвигает 2 в X, 3 сдвигает 3 в и так далее.Это просто базовая форма, поддерживающая до 9 аргументов, чтобы передать идею.

... теперь вы можете генерировать различные утилиты макросов, как эта:

#define GLUE(A,B) GLUE_I(A,B)
#define GLUE_I(A,B) A##B
#define TRANSFORM_CD(MACRO, ...) GLUE(TRANSFORM_CD_,COUNT(__VA_ARGS__))(MACRO,__VA_ARGS__)

#define TRANSFORM_CD_1(MACRO,X) MACRO(X)
#define TRANSFORM_CD_2(MACRO,X,...) MACRO(X),TRANSFORM_CD_1(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_3(MACRO,X,...) MACRO(X),TRANSFORM_CD_2(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_4(MACRO,X,...) MACRO(X),TRANSFORM_CD_3(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_5(MACRO,X,...) MACRO(X),TRANSFORM_CD_4(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_6(MACRO,X,...) MACRO(X),TRANSFORM_CD_5(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_7(MACRO,X,...) MACRO(X),TRANSFORM_CD_6(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_8(MACRO,X,...) MACRO(X),TRANSFORM_CD_7(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_9(MACRO,X,...) MACRO(X),TRANSFORM_CD_8(MACRO,__VA_ARGS__)

Концептуально TRANSFORM_CD предназначен для "преобразования" списка с разделителями-запятыми (аргументы 2 и выше) в другой список с разделителями-запятыми, применяя к нему макрос.В этом случае мы начнем с разделенного запятыми списка базовых имен (что вы называете IDENT здесь) и применим к нему один из ваших макросов преобразования;например, TRANSFORM(CONCATH, TRIG, CONT, DUPL) расширяется до CODE_GEN_TRIG_HDR, CODE_GEN_CONT_HDR, CODE_GEN_DUPL_HDR.

Далее нам нужно что-то для генерации вызовов с несколькими макросами и одним и тем же набором параметров;как эта утилита:

#define INVOKE_ALL(TUPLE_, ...) GLUE(INVOKE_ALL_,COUNT(__VA_ARGS__))(TUPLE_,__VA_ARGS__)

#define INVOKE_ALL_1(TUPLE_, X) X TUPLE_
#define INVOKE_ALL_2(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_1(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_3(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_2(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_4(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_3(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_5(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_4(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_6(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_5(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_7(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_6(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_8(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_7(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_9(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_8(TUPLE_,__VA_ARGS__)

Здесь TUPLE_ предназначен для того, чтобы быть списком аргументов в скобках (это делает утилиту немного более общей, чем требование, чтобы она поддерживала ровно три параметра);и каждый другой параметр представляет макрос, который будет вызываться с этими аргументами.

Комбинируя их, это:

INVOKE_ALL((p1 a1, p2 a2, p3 a3),TRANSFORM_CD(CONCATH,MATH,TRIG,CONT,DUPL))

... должно быть расширено (переформатировано для ясности):

CODE_GEN_TRIG_HDR(p1 a1, p2 a2, p3 a3)
CODE_GEN_CONT_HDR(p1 a1, p2 a2, p3 a3)
CODE_GEN_DUPL_HDR(p1 a1, p2 a2, p3 a3)

... и если вы действительно хотите, вы можете сохранить форму и функцию MASTER_MACRO, просто сделав ее вариативной, например:

#define MASTER_MACRO(PARAM1, PARAM2, PARAM3, ...) \
    INVOKE_ALL((PARAM1, PARAM2, PARAM3),TRANSFORM_CD(CONCATH,__VA_ARGS__)) \
    INVOKE_ALL((PARAM1, PARAM2, PARAM3),TRANSFORM_CD(CONCATC,__VA_ARGS__))

демонстрация в стеке-crooked

...