编写 vargarg C 预处理器宏,它用它的每个参数调用一些其他宏等等

Write vargarg C preprocessor macro which calls some other macro with each of its arguments and more

我需要在我的代码中包含有关结构的元信息。因此,我编写了一些 C 结构(用于存储元信息)和 C 预处理器宏的组合来初始化这些结构,而无需大量样板代码。现在它看起来像这样(真的,我存储了很多关于字段的信息,但这段代码足以解决我的问题):

#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>

struct meta_field {
  const char *name;
  size_t offset;
};

struct meta_struct {
  const char *name;
  size_t size;
  struct meta_field fields[];
};

#define META_STRUCT_BEGIN(NAME)               \
  static struct meta_struct s_meta_##NAME = { \
    .name = "" #NAME,                         \
    .size = sizeof(struct NAME),              \
    .fields = {
#define META_STRUCT_END()  ,{NULL, 0}}}
#define META_FIELD(NAME)   { .name = "" #NAME, .offset = offsetof(struct CURRENT_META_STRUCT, NAME) }


struct example {
  int i;
  double d;
};

#define CURRENT_META_STRUCT example
META_STRUCT_BEGIN(CURRENT_META_STRUCT)
  META_FIELD(i),
  META_FIELD(d)
META_STRUCT_END();
#undef CURRENT_META_STRUCT

有效。但它仍然有很多样板文件:#define CURRENT_META_STRUCTCURRENT_META_STRUCT 作为 META_STRUCT_BEGIN() 的参数的用法、CURRENT_META_STRUCT#undef 的用法——所有这些看起来都很丑陋,恕我直言.

我知道,一个宏定义一个宏是不可能的(所以,META_STRUCT_BEGIN()不能定义CURRENT_META_STRUCT)。但是看看 C 预处理器和 Boost-PP 中的 Branf*ck 实现,我认为可以实现如下所示的东西:

META_STRUCT(example,
  META_FIELD(i),
  META_FIELD(d)
)

但我无法理解这个预处理器魔法。

有人可以帮助我吗?

我已经阅读了 https://github.com/orangeduck/CPP_COMPLETE,但没有帮助。

此外,它被标记为 的副本,但问题是我需要向所有生成的宏 "calls" 添加 "default" 参数。

这里有一些草图,我想要实现的目标:

#define META_FIELD_IMPL(STRUCT, NAME) { .name = "" #NAME, .offset = offsetof(struct STRUCT, NAME) }

#define META_FIELD(NAME)  NAME

#define META_STRUCT(NAME, ... )               \
  static struct meta_struct s_meta_##NAME = { \
    .name = "" #NAME,                         \
    .size = sizeof(struct NAME),              \
    .fields = {                               \
      /* Each ARG from __VA_ARGS__ is META_FIELD(x) and                 \
       * I need to call META_FIELD_IMPL(NAME, <Expansion of META_FIELD>) \
       * for each ARG from _VA_ARGS_ here */                            \
      {NULL, 0}                               \
     }}

现在在这个例子中 META_FIELD() 只有一个参数:NAME,但在实际系统中 META_FIELD() 有 6 个参数和一些提供 "default" 一般情况下的值,所以 META_FIELD() 是必需的,不能用 NAME 本身代替。

最后一个复杂的问题:这样的 META_STRUCT() 可以作为 META_FIELD() 的参数调用,因为一些字段包含指向嵌套元结构的指针!现在它是通过为所有嵌套子结构声明命名对象来完成的,但我也想避免它!我明白,嵌套的深度可能会受到某个任意常数的限制,没关系。

更新:我添加了这个例子,说明我想输入什么以及我想在预处理器之后得到什么。我不知道如何实现所有这些宏(标记为 /* ??? */)。而且,是的,我已经检查过,手动编码 "end result" 可以正常编译。

enum meta_type { mt_end, mt_int, mt_string, mt_struct };

struct meta_struct;

struct meta_field {
  const char *name;
  enum meta_type type;
  size_t offset;
  struct meta_struct *child;
};

struct meta_struct {
  const char *name;
  size_t size;
  struct meta_field *fields;
};

#define META_STRUCT(NAME, ...)  static struct meta_struct s_meta_##name = { .name = #NAME, .size = sizeof(struct NAME), .fileds = (struct meta_field[]){ /* ??? */, {NULL, mt_end, 0, NULL}}}
#define META_FIELD_IMPL0(STRUCT, NAME, TYPE, CHILD) { .name = #NAME, .type = TYPE, .offset = offsetof(STRUCT, NAME), .child = CHILD }
#define META_FIELD_IMPL1(NAME, TYPE, CHILD)  /* ??? */

#define META_FIELD(NAME, TYPE)       META_FIELD_IMPL1(NAME, TYPE, NULL)
#define META_FIELD_SUB(NAME, CHILD)  META_FIELD_IMPL1(NAME, mt_struct, CHILD)
#define META_SUBSTRUCT(NAME, ...)  (struct meta_struct){ .name = #NAME, .size = sizeof(struct NAME), .fileds = (struct meta_field[]){ /* ??? */, {NULL, mt_end, 0, NULL}}}

/* Example of "input": */
struct child {
  int i;
};

struct parent {
  int i;
  struct child c;
  const char *s;
};

META_STRUCT(parent,
  META_FIELD(i, mt_int),
  META_FIELD_SUB(c, 
    META_SUBSTRUCT(child,
      META_FIELD(i, mt_int)
    )
  ),
  META_FIELD(s, mt_string)
);

/* This should give this */
static struct meta_struct s_meta_parent = {
  .name = "parent",
  .size = sizeof(struct parent),
  .fields = (struct meta_field[]) {
    { .name = "i", .type = mt_int, .offset = offsetof(struct parent, i), .child = NULL },
    { .name = "c", .type = mt_struct, .offset = offsetof(struct parent, c), .child = &(struct meta_struct){
        .name = "child",
        .size = sizeof(struct child),
        .fields = (struct meta_field[]) {
          { .name = "i", .type = mt_int, .offset = offsetof(struct child, i), .child = NULL },
          {NULL, mt_end, 0, NULL}
        }
      }
    },
    { .name = "s", .type = mt_string, .offset = offsetof(struct parent, s), .child = NULL },
    {NULL, mt_end, 0, NULL}
  }
};

使用 __VA_ARGS__ 怎么样?下面的 Codesnip 接受最多 4 个字段,但您可以通过复制和粘贴一些代码行来扩展到 5 个、6 个或更多(有关详细信息,请参阅 )。

头文件:

#define META_FIELD1(st,x1) { .name = #x1, .offset = offsetof(struct st, x1) }
#define META_FIELD2(st,x1,x2) META_FIELD1(st,x1), META_FIELD1(st, x2)
#define META_FIELD3(st,x1,x2,x3) META_FIELD1(st,x1), META_FIELD2(st,x2,x3)
#define META_FIELD4(st,x1,x2,x3,x4) META_FIELD1(st,x1), META_FIELD3(st,x2,x3,x4)
#define META_FIELD(st, ...)     META_FIELD4(st, ##__VA_ARGS__) // limit to 4

#define META_STRUCT(st, ...) \
    static struct meta_struct meta_struct_##st = { \
        .name = #st, \
        .size = sizeof(struct st), \
        .fields = { \
            META_FIELD(st, ##__VA_ARGS__) \
        } \
    } \

C 文件:

struct sample {
    int i;
    double d;
    const char *test;
    float t;
};

META_STRUCT(sample, i, d, test, t);

/*
 * 
 */
int main(int argc, char** argv) {
    printf("struct %s { %s, %s, %s, %s } \n", 
            meta_struct_sample.name, 
            meta_struct_sample.fields[0].name, 
            meta_struct_sample.fields[1].name, 
            meta_struct_sample.fields[2].name, 
            meta_struct_sample.fields[3].name);

    return (EXIT_SUCCESS);
}

输出应该是:struct sample { i, i, test, t }

这是一个方法。

基本实用程序

#define GLUE(A,B) GLUE_I(A,B)
#define GLUE_I(A,B) A##B
#define COUNT(...) COUNT_I(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1,)
#define COUNT_I(V,_9,_8,_7,_6,_5,_4,_3,_2,X,...) X

这是一个间接粘合宏(允许扩展参数)和一个计数宏。这里的 count 宏只支持 9 个参数,但扩展模式很容易。这只是扩展到传递的参数数量。我假设您以前见过这个,但以防万一,这只是通过参数转换起作用的;它间接扩展了参数,并且每个额外的参数移动到 1 以上都会取代数字列表,直到它扩展到 "above" X 为止。

#define FIRST(...) FIRST_I(__VA_ARGS__,)
#define FIRST_I(X,...) X
#define REST(X,...) __VA_ARGS__
#define EXPAND(...) __VA_ARGS__

...这些是对元组的操作。出于此答案的目的,tuple 是括号中的一组标记。 FIRSTREST 使用元组作为数据类型(应该很明显;FIRST 间接获取第一个元素,REST 扩展到除此以外的所有元素)。 EXPAND 展开一个元组。

下一个实用程序宏:

#define FOREACH(MACRO_,DATA_,TUPLE_) GLUE(FOREACH_I_,COUNT TUPLE_)(MACRO_,DATA_,TUPLE_)

#define FOREACH_I_1(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_)
#define FOREACH_I_2(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_1(MACRO_,DATA_,(REST TUPLE_))
#define FOREACH_I_3(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_2(MACRO_,DATA_,(REST TUPLE_))
#define FOREACH_I_4(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_3(MACRO_,DATA_,(REST TUPLE_))
#define FOREACH_I_5(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_4(MACRO_,DATA_,(REST TUPLE_))
#define FOREACH_I_6(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_5(MACRO_,DATA_,(REST TUPLE_))
#define FOREACH_I_7(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_6(MACRO_,DATA_,(REST TUPLE_))
#define FOREACH_I_8(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_7(MACRO_,DATA_,(REST TUPLE_))
#define FOREACH_I_9(MACRO_,DATA_,TUPLE_) MACRO_(DATA_,FIRST TUPLE_) FOREACH_I_8(MACRO_,DATA_,(REST TUPLE_))

这是关键宏;它实际上用于将结构的类型应用到每个字段的较低级别的宏中。同样,此特定代码每个级别仅适用于 9 个字段,但是如何扩展它应该是显而易见的(您可能希望支持 COUNT 宏可以数到的级别)。

FOREACH的第三个参数应该是元组; FOREACH 的工作是在 DATA_TUPLE_ 的每个元素上调用您的宏。例如,FOREACH(FIELD,parent,(i,j,k)) 扩展为 FIELD(parent,i) FIELD(parent,j) FIELD(parent,k)

特定实用程序的概念

特定实用程序的核心调用结构使用元组数据类型;当与 FOREACH 宏一起使用时,底层调用会将外部结构类型适当地柯里化到每个调用中。调用结构如下所示:

<b>META_STRUCT(</b>类型<b>,</b> FIELD[<b>,</b> FIELD[ ... ] ] <b>)</b>

...其中每个 FIELD 是一个三元组:

<b>(</b>名称<b>,</b>类型<b>,</b>CHILD <b>)</b>

...并且每个 CHILD 都是一个元组,它被扩展为一个 .child 赋值。 META_SUBSTRUCT 的工作原理类似。所以根据这个调用结构,你的例子实际上会被这样调用:

META_STRUCT(parent, (i, mt_int, (NULL)), (c, mt_struct, META_SUBSTRUCT(child,(i,mt_int,(NULL)))),(s, mt_string, (NULL)));

实际上,META_SUBSTRUCT实际上会完全展开。它的扩展将是一个 "tuple of tokens" 作为 child 赋值;也就是说,META_SUBSTRUCT 将扩展到用括号包围的子结构中的实际赋值。 FOREACH 宏应用于此构造,将上述 3 元组的外部结构和三个参数柯里化为 4 参数宏 APPLY_UTILI_FIELD.

用法反映了您的用法并采用了此调用结构。例如,META_FIELD(i, mt_int) 只是一个扩展为 (i, mt_int, (NULL)).

的宏

特定实用程序

#define META_STRUCT_BEGIN(NAME_) \
   static struct meta_struct s_meta_##NAME_ = {  \
      .name = #NAME_,  \
      .size = sizeof(struct NAME_),  \
      .fields = (struct meta_field[]) { 

#define META_STRUCT(NAME_,...) \
   META_STRUCT_BEGIN(NAME_) \
      FOREACH(APPLY_META_STRUCT_MACRO,NAME_,(__VA_ARGS__)) \
   META_STRUCT_END()

#define META_STRUCT_END()  \
         {NULL, mt_end, 0, NULL}}}

#define META_SUBSTRUCT(NAME_,...) \
   ( META_SUBSTRUCT_BEGIN(NAME_) \
        FOREACH(APPLY_META_STRUCT_MACRO,NAME_,(__VA_ARGS__)) \
     META_SUBSTRUCT_END() )

#define META_SUBSTRUCT_BEGIN(NAME_) \
   &(struct meta_struct) { \
      .name = #NAME_, \
      .size = sizeof(struct NAME_), \
      .fields = (struct meta_field[]) {

#define META_SUBSTRUCT_END() \
    {NULL, mt_end, 0, NULL}}}

#define APPLY_META_STRUCT_MACRO(DATA_, ARG_) APPLY_UTIL_FIELD(DATA_, EXPAND ARG_)
#define APPLY_UTIL_FIELD(...) APPLY_UTILI_FIELD(__VA_ARGS__)
#define APPLY_UTILI_FIELD( STRUCT_, FIELD_, TYPE_, CHILD_) \
   { .name = #FIELD_, .type = TYPE_, .offset = offsetof(struct STRUCT_, FIELD_), .child = EXPAND CHILD_ },

#define META_FIELD(NAME_, TYPE_) (NAME_, TYPE_, (NULL))
#define META_FIELD_SUB(NAME_, SUB_) (NAME_, mt_struct, SUB_)

演示

Showing your example

备注

嵌套

这些宏允许在没有 运行 的情况下无限期地嵌套 META_SUBSTRUCT 调用到蓝色油漆中,因为所有这些宏最终都在参数替换阶段被评估(... 参数提到 __VA_ARGS__ 导致所有参数扩展;META_SUBSTRUCT 的每个级别在这种情况下都是顶级扩展;这种情况递归)。

你的六个领域

鉴于您展示的示例字段少于您使用的字段,您需要专门调整用户定义的 META_FIELDMETA_FIELD_SUB 等宏以生成 k -tuples 而不是 3-tuples(其中 k 是你需要的)。然后,只需调整 APPLY_UTILI_FIELD 的参数和扩展以匹配。

微软兼容性

此代码采用标准 C 预处理器。如果您正在专门处理 MSVC(没有),它将无法工作。具体来说,MSVC 未能按预期扩展 __VA_ARGS__;如果您关心 在 MSVC 上工作,请将这三个对应的宏更改为:

#define COUNT(...) EXPAND(COUNT_I(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1,))
#define FIRST(...) EXPAND(FIRST_I(__VA_ARGS__,))
#define APPLY_UTIL_FIELD(...) EXPAND(APPLY_UTILI_FIELD(__VA_ARGS__))

结果也适用于符合标准的 CPP。