编写vargarg C预处理器宏,它使用每个参数调用其他宏以及更多

时间:2017-07-04 16:09:44

标签: c c-preprocessor preprocessor-directive

我需要在代码中获得有关结构的元信息。因此,我编写了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_STRUCT,使用CURRENT_META_STRUCT作为META_STRUCT_BEGIN()的参数,#undef作为CURRENT_META_STRUCT的使用 - 所有这些看起来很难看,恕我直言

我知道,按宏定义宏是不可能的(因此,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,但它没有帮助。

此外,它被标记为this的副本,但问题是我需要添加&#34;默认&#34;所有生成的宏&#34;调用&#34;。

的参数

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

#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个参数,而某些帮助宏提供&#34;默认& #34;常见案例的值,因此META_FIELD()是必要的,NAME本身无法取代。

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

更新:我添加了这个我要输入内容的示例以及我希望在预处理器之后获得的内容。我无法弄清楚如何实现所有这些宏(标有/* ??? */)。并且,是的,我已经检查过,手动编码&#34;最终结果&#34;编译好。

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}
  }
};

2 个答案:

答案 0 :(得分:1)

这是一种方法。

基本实用程序

#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以上取代了数字列表,直到某些东西“高于”X,它扩展为。

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

......这些是对元组的操作。出于本答案的目的,元组是括号中的一组标记。 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宏一起使用时,底层调用会将外部结构类型适当地调整到每个调用中。调用结构如下所示:

META_STRUCT(type , FIELD[ , FIELD[ ... ] ] )

...每个FIELD都是3元组:

(name , type , CHILD )

...每个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实际上会完全扩展。它的扩展将是一个“代币元组”,充当子任务;也就是说,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次调用而不会进入蓝色绘制,因为所有这些宏最终都会在参数替换阶段进行评估(提及...的{​​{1}}参数会导致所有参数扩展; __VA_ARGS__的每个级别都是此场景中的顶级扩展;此条件是递归的。)

你的六个字段

鉴于您展示的字段数少于您使用的字段,您需要专门调整用户定义的META_SUBSTRUCTMETA_FIELD等宏以生成META_FIELD_SUB - 元组而不是3元组(其中k是你需要的)。然后,只需调整k的参数和扩展即可。

Microsoft兼容性

此代码假定使用标准C预处理器。如果您正在专门研究MSVC(没有MSVC),它将无法正常工作。具体来说,MSVC无法扩展APPLY_UTILI_FIELD。如果您关心在MSVC上工作,请将这三个宏的相应宏更改为:

__VA_ARGS__

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

答案 1 :(得分:0)

如何使用__VA_ARGS__?下面的代码压区接受最多4个字段,但您可以通过复制并粘贴一些代码行扩展到5个,6个或更多个字段(有关详细信息,请参阅here)。

标题文件:

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