首页 > 解决方案 > C 预处理器如何从宏到结构定义

问题描述

当我在寻找一种在 C 中进行反射的方法时,我找到了这个答案https://stackoverflow.com/a/31908340/6784916

在他的回答中,他提到了 metaresc 库,并展示了如何使用它的示例:

TYPEDEF_STRUCT (point_t,
                double x,
                double y
                );

int main (int argc, char * argv[])
{
  point_t point = {
    .x = M_PI,
    .y = M_E,
  };

  ...
}

TYPEDEF_STRUCT 在https://github.com/alexanderchuranov/Metaresc/blob/master/src/metaresc.h的第 237 行定义

我尝试提取宏的来源,但我不确定我是否遗漏了什么,因为它太复杂了。

#define TYPEDEF_STRUCT(...) P00_TYPEDEF (STRUCT, __VA_ARGS__)

#ifndef MR_MODE
#define MR_MODE_UNDEFINED
#define MR_MODE PROTO
#endif
#include <mr_protos.h>
#ifdef MR_MODE_UNDEFINED
#undef MR_MODE_UNDEFINED
#undef MR_MODE
#endif

#define MR_IS_MR_MODE_EQ_MR_MODE 0
#define P00_TYPEDEF(...)                        \
  MR_IF_ELSE (MR_PASTE2 (MR_IS_MR_MODE_EQ_, MR_MODE))           \
  (P00_TYPEDEF_MODE (MR_MODE, __VA_ARGS__))             \
  (P00_TYPEDEF_MODE (PROTO, __VA_ARGS__) P00_TYPEDEF_MODE (DESC, __VA_ARGS__))

#define MR_IGNORE(...)
#define MR_IDENT(...) __VA_ARGS__
#define MR_IF_ELSE_CASE_0(...) __VA_ARGS__ MR_IGNORE
#define MR_IF_ELSE_CASE_1(...) MR_IDENT
#define MR_IF_ELSE(...) MR_PASTE2 (MR_IF_ELSE_CASE_, MR_IS_EQ_0 (__VA_ARGS__))

#define MR_PASTE2(...) MR_PASTE2_ (__VA_ARGS__)
#define MR_PASTE2_(_0, _1) _0 ## _1

#define MR_IS_0_EQ_0 ,
#define MR_IS_EQ_0_CASE_011 ,

#define MR_GET_SECOND(_0, ...) __VA_ARGS__
#define MR_IS_EQ_0(...) MR_IS_EQ_0_ (__VA_ARGS__) /* evaluate arguments */
#define MR_IS_EQ_0_(...) MR_IS_EQ_0__ ((__VA_ARGS__), (MR_PASTE2 (MR_IS_0_EQ_, __VA_ARGS__)))
#define MR_IS_EQ_0__(ARGS, ARGS_EQ_0)                   \
  MR_HAS_COMMA (MR_PASTE4 (MR_IS_EQ_0_CASE_,                \
               /* test if there is just one argument, eventually a zero */ \
               MR_HAS_COMMA ARGS,               \
               /* test if MR_IS_0_EQ_ together with the argument adds a comma */ \
               MR_HAS_COMMA ARGS_EQ_0,          \
               /* test that there is nothing after comma */ \
               MR_IS_EMPTY (MR_GET_SECOND ARGS_EQ_0)))

#define P00_TYPEDEF_MODE(P00_MODE, P00_TYPE, ...)           \
  P00_TYPEDEF_MODE_ (P00_MODE, P00_TYPE,                \
             ATTRIBUTES (P00_GET_ATTRIBUTES (__VA_ARGS__)), \
             P00_GET_NON_ATTRIBUTES (__VA_ARGS__))
#define P00_TYPEDEF_MODE_(...) P00_TYPEDEF_MODE__ (__VA_ARGS__)
#define P00_TYPEDEF_MODE__(P00_MODE, P00_TYPE, ATTR_META_RES, ...)  \

#define P00_GET_ATTRIBUTES(...) MR_FOREACH (P00_EXTRACT_ATTRIBUTES, __VA_ARGS__)
#define P00_GET_NON_ATTRIBUTES(...) MR_FOREACH (P00_EXTRACT_NON_ATTRIBUTES, __VA_ARGS__)

#define MR_FOREACH(X, ...) MR_PASTE2 (MR_FOREACH, MR_NARG (__VA_ARGS__)) (X, __VA_ARGS__)
#define MR_FOR(NAME, N, OP, FUNC, ...) MR_PASTE2 (MR_FOR, N) (NAME, OP, FUNC, __VA_ARGS__)

#define P00_TYPEDEF_ATTR_STRUCT TYPEDEF_ATTR
#define P00_TYPEDEF_ATTR_UNION TYPEDEF_ATTR
#define P00_TYPEDEF_ATTR_ENUM TYPEDEF_ATTR
#define P00_TYPEDEF_ATTR_CHAR_ARRAY(P00_MODE, P00_TYPE, ATTR_META_RES, P00_TYPE_NAME, SIZE, ...) MR_PASTE2 (MR_TYPEDEF_CHAR_ARRAY_, P00_MODE) (P00_TYPE_NAME, SIZE, MR_PASTE2 (P00_REMOVE_, ATTR_META_RES), __VA_ARGS__)
#define P00_TYPEDEF_ATTR_FUNC(P00_MODE, P00_TYPE, ATTR_META_RES, RET_TYPE, P00_TYPE_NAME, ARGS, ...) MR_PASTE2 (MR_TYPEDEF_FUNC_, P00_MODE) (RET_TYPE, P00_TYPE_NAME, ARGS, MR_PASTE2 (P00_REMOVE_, ATTR_META_RES), __VA_ARGS__)

#define P00_UNFOLD(PREFIX, P00_TYPE, P00_MODE, ...) MR_PASTE4 (PREFIX, P00_TYPE, _, P00_MODE) (__VA_ARGS__)

#define TYPEDEF_ATTR(P00_MODE, P00_TYPE, ATTR_META_RES, P00_TYPE_NAME, ...) \
  P00_UNFOLD (MR_TYPEDEF_, P00_TYPE, P00_MODE, P00_TYPE_NAME, MR_PASTE2 (P00_GET_FIRST_, ATTR_META_RES)) \
  MR_FOR ((P00_MODE, P00_TYPE_NAME), MR_NARG (__VA_ARGS__), MR_SER, MR_PASTE3 (P00_, P00_TYPE, _HANDLER), __VA_ARGS__) \
  P00_UNFOLD (MR_END_, P00_TYPE, P00_MODE, P00_TYPE_NAME, MR_PASTE2 (P00_GET_OTHER_, ATTR_META_RES))

# define P00_IS_ATTRIBUTES_EQ_ATTRIBUTES(...) 0 /* help macro for ATTRIBUTES test IF clause */
#define P00_REMOVE_ATTRIBUTES(...) __VA_ARGS__
#define P00_GET_FIRST_ATTRIBUTES(FIRST, ...) FIRST /* extract typedef attributes */
#define P00_GET_OTHER_ATTRIBUTES(FIRST, ...) __VA_ARGS__ /* extract typedef meta information */

我只想知道如何进行宏调用,例如

TYPEDEF_STRUCT (point_t,
                    double x,
                    double y
                );

扩展到那个

typedef struct point_t {
          double x;
          double y;
} point_t;

标签: creflectionmacros

解决方案


我不确定您为什么要将标头与库的其余部分分离。在 TYPEDEF_STRUCT 中可能使用了 metaresc.h 的前 700 行,因此您将需要该文件的大部分内容。即使它对您没有帮助,只是因为宏的结果是 typedef + 序列化所需的元数据。序列化函数在库中实现,因此将元数据生成与使用该元数据的代码分离是没有意义的。

您可以通过预处理器运行示例并评估结果。这将使您了解我所说的元数据是什么意思。

如果你真的有兴趣了解 TYPEDEF_STRUCT 宏的实现细节,那就为 100+ 层的嵌套宏做好准备吧。作为第一个练习,您可以从这里开始https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/并继续使用https://p99.gforge.inria.fr中的其他宏技巧/


推荐阅读