Как сделать макрос C развернутым до структуры и функции, используя этот метод - PullRequest
2 голосов
/ 26 марта 2020

Я хочу иметь макрос

#define MY_STRUCT( /* ... /*) /* ... */

То, что я хочу назвать таким образом

MY_STRUCT(point, double x, int y);

, который расширяется до этого

typedef struct {
    double x;
    int y;
} point;

void init_point(point *p) {
     p->x = load_double_from_somewhere();
     p->y = load_int_from_somewhere();
}

Макрос должен быть в состоянии обработать любое количество параметров, которые я даю. Я пытаюсь сгенерировать привязки данных для объектов модели автоматически через макросы, потому что я пишу функции отображения вручную, и это утомительно и повторяется (структура xml - C). Я знаю, что это возможно, но я не могу понять, как.

Ответы [ 4 ]

1 голос
/ 27 марта 2020

Если вы хотите автоматически генерировать исходный код, препроцессор - худший инструмент, который вы можете получить.

И вы абсолютно не можете создать какой-либо макрос для генерации структуры с любым числом полей; препроцессор C не поддерживает циклы или рекурсию.

Но если достаточно максимум 8 полей / аргументов, хотя NANDANDARD , это будет сделано в g cc и clang:

#define MY_STRUCT(name, ...)  struct name { C2SC(__VA_ARGS__,,,,,,,,,) }
#define C2SC(a, b, c, d, e, f, g, h, ...)       a; b; c; d; e; f; g; h;

MY_STRUCT(foo, int bar, int baz, void *quux, void *xuq, char sq[12]);

Это намеренно создает struct name { ... }, а не typedef struct { ... } name, как в OP: он уже достаточно тупой даже без макроса MY_STRUCT, который фактически генерирует typedef.

1 голос
/ 26 марта 2020

Я не уверен, возможно ли извлечь x из double x (если вы не знаете всех возможных типов, которые могут иметь ваши переменные).

Кроме того, для перебора списков через запятую с использованием препроцессора требуется написание шаблонных макросов (я думаю, O(n) из них). Boost.Preprocessor может помочь с этим, но вы можете полностью избежать шаблонов, если вы используете другой синтаксис:

MY_STRUCT(point, (double,x)(double,y))

Реализация:

#define MY_STRUCT(name, seq) \
    typedef struct { \
        MY_STRUCT_impl_end(MY_STRUCT_impl_decl_loop_a seq) \
    } name; \
    void init_point(name *p) { \
        MY_STRUCT_impl_end(MY_STRUCT_impl_load_loop_a seq) \
    }

#define MY_STRUCT_impl_end(...) MY_STRUCT_impl_end_(__VA_ARGS__)
#define MY_STRUCT_impl_end_(...) __VA_ARGS__##_end

#define MY_STRUCT_impl_decl_loop(type, name) type name;
#define MY_STRUCT_impl_decl_loop_a(...) MY_STRUCT_impl_decl_loop(__VA_ARGS__) MY_STRUCT_impl_decl_loop_b
#define MY_STRUCT_impl_decl_loop_b(...) MY_STRUCT_impl_decl_loop(__VA_ARGS__) MY_STRUCT_impl_decl_loop_a
#define MY_STRUCT_impl_decl_loop_a_end
#define MY_STRUCT_impl_decl_loop_b_end

#define MY_STRUCT_impl_load_loop(type, name) p->name = load_##name##_from_somewhere();
#define MY_STRUCT_impl_load_loop_a(...) MY_STRUCT_impl_load_loop(__VA_ARGS__) MY_STRUCT_impl_load_loop_b
#define MY_STRUCT_impl_load_loop_b(...) MY_STRUCT_impl_load_loop(__VA_ARGS__) MY_STRUCT_impl_load_loop_a
#define MY_STRUCT_impl_load_loop_a_end
#define MY_STRUCT_impl_load_loop_b_end
1 голос
/ 26 марта 2020

Примечание: Я только отвечаю на вопрос, как написать определение struct. Я не вижу способа заставить препроцессор автоматически генерировать код сериализации и десериализации по нескольким причинам. Во-первых, невозможно извлечь токен - имя члена - из аргумента макроса, даже если вы знаете, какой токен извлечь. Во-вторых, вы можете использовать функцию C11 _Generic для создания вызовов предопределенных функций сериализации / десериализации с указанием типа c. Но для вызова _Generic требуется полный список возможностей, поэтому наращивание возможностей будет невозможно. Я думаю, вы обнаружите, что существующие системы, которые автоматически генерируют код сериализации / десериализации, зависят от внешних генераторов кода. Это гораздо более гибкая (и, вероятно, более простая) стратегия.


Итак, вернемся к автогенерации объявлений структуры. Как уже упоминалось, это действительно возможно, только если вы генерируете макросы N , по одному на каждое количество аргументов. Также вам нужен способ вычисления N , для которого может потребоваться больше автоматически генерируемых макросов. В общем, гораздо проще использовать фреймворк, такой как P99 .

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

Мы начнем с макроса, который будет вычислять количество аргументов в вызове variadi c. Обратите внимание, что это будет правильно возвращать 0, если нет аргументов variadi c. Надежное решение, такое как P99, справится с этим правильно. Но в этом случае это не имеет значения, потому что struct без членов не разрешено C, поэтому мы знаем, что должен быть хотя бы один аргумент. (Это не было бы правдой в C ++.)

#define ARG9_(a1, a2, a3, a4, a5, a6, a7, a8, a9, ...) a9
#define NARGS(...) ARG9_(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)

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

#define S8_(first, ...) first; S7_(__VA_ARGS__)
#define S7_(first, ...) first; S6_(__VA_ARGS__)
#define S6_(first, ...) first; S5_(__VA_ARGS__)
#define S5_(first, ...) first; S4_(__VA_ARGS__)
#define S4_(first, ...) first; S3_(__VA_ARGS__)
#define S3_(first, ...) first; S2_(__VA_ARGS__)
#define S2_(first, ...) first; S1_(__VA_ARGS__)
#define S1_(first) first;

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

#define PASTE3(a, b, c) PASTE3_(a, b, c)
#define PASTE3(a, b, c) a ## b ## c

Наконец, макрос, который создает структуру

#define MY_STRUCT(name, ...)                       \
   typedef struct name name;                       \
   struct name {                                   \
     PASTE3(S, NARGS(__VA_ARGS__), _)(__VA_ARGS__) \
   };

И теперь мы можем попробуйте все это:

MY_STRUCT(s1, int a);
MY_STRUCT(s2, int a, double b);
MY_STRUCT(s3, const char* s, int t[17], double sum);
MY_STRUCT(s4, char a, char b, char c, char d);
MY_STRUCT(s5, char a, char b, char c, char d, char e);
MY_STRUCT(s6, char a, char b, char c, char d, char e, char f);
MY_STRUCT(s7, char a, char b, char c, char d, char e, char f, char g);
MY_STRUCT(s8, char a, char b, char c, char d, char e, char f, char g, short h);

Вот что gcc -E производит, учитывая все вышеперечисленное: (Примечание: я не могу комментировать, работает ли это на различных версиях MSV C. Но это все стандартно C99.)

# 1 "nargs.h"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "nargs.h"
# 22 "nargs.h"
typedef struct s1 s1; struct s1 { int a; };
typedef struct s2 s2; struct s2 { int a; double b; };
typedef struct s3 s3; struct s3 { const char* s; int t[17]; double sum; };
typedef struct s4 s4; struct s4 { char a; char b; char c; char d; };
typedef struct s5 s5; struct s5 { char a; char b; char c; char d; char e; };
typedef struct s6 s6; struct s6 { char a; char b; char c; char d; char e; char f; };
typedef struct s7 s7; struct s7 { char a; char b; char c; char d; char e; char f; char g; };
typedef struct s8 s8; struct s8 { char a; char b; char c; char d; char e; char f; char g; short h; };
1 голос
/ 26 марта 2020

Я не знаю, для чего вам это нужно, но ...

#define x(a,b,c)    struct a {b;c;}

x(mystr, int m, double n);

, и результат:

struct mystr {int m;double n;};

или

#define x(a,b,c)    typedef struct {b;c;} a

x(mystr, int m, double n);

и результат

typedef struct {int m;double n;} mystr;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...