C MacroMagic-结构定义

时间:2018-09-26 14:14:36

标签: c c-preprocessor

我正在寻找一种定义结构的解决方案,用户可以如示例中那样启用/禁用结构成员(伪代码):

#define DEF_STRUCT_1(NAME,VAL1,VAL2)      \
    struct my_struct_t                    \
    {                                     \
      #if(NAME == TRUE)                   \
        bool name;                        \
      #endif                              \
      #if(VAL1 == TRUE)                   \
        bool val1;                        \
      #endif                              \
      #if(VAL2 == TRUE)                   \
        bool val2;                        \
      #endif                              \
    } instance1

void main() {
  DEF_STRUCT_1(TRUE,FALSE,TRUE);

  instance1.name = true;
  //instance1.val1 = false; // error, unavailable
  instance1.val2 = false;
}

2 个答案:

答案 0 :(得分:5)

我不确定这有什么用,但是以下内容可以满足您的要求:

#define CONDITIONAL_TRUE(code) code
#define CONDITIONAL_FALSE(code)

#define DEF_STRUCT_1(NAME,VAL1,VAL2)      \
    struct my_struct_t                    \
    {                                     \
      CONDITIONAL_##NAME(bool name;)      \
      CONDITIONAL_##VAL1(bool val1;)      \
      CONDITIONAL_##VAL2(bool val2;)      \
    } instance1

int main() {
  DEF_STRUCT_1(TRUE,FALSE,TRUE);

  instance1.name = true;
  //instance1.val1 = false; // error, unavailable
  instance1.val2 = false;
}

所有TRUE / FALSE参数必须在编译时可用。而且,如果您希望在同一程序中使用这些参数的多个版本,则还应该使结构名称成为参数。

由于您说这是针对库的,因此尚不清楚您如何计划库代码能够访问此结构,因为它需要知道哪些成员可用。这大大降低了该方法的实用性。

库使用的一种更常见的方法是拥有一个config.h文件,该文件可由库用户编辑,并带有诸如#define USE_NAME_MEMBER 1之类的定义。然后,您可以使用struct伪指令创建普通的#if定义:

//in mylibrary.h:
#include <mylibrary_config.h>

struct my_struct_t {
    #if USE_NAME_MEMBER
        bool name;
    #endif
    /...
};

然后,您还将在访问#if成员的任何库代码周围放置name指令。

答案 1 :(得分:0)

鉴于给定的条件,在编译时需要以不同的方式生成结构,因此您将面临一个问题,即所有使用该结构的代码都需要进行相应的修改。编译器开关(#ifdef FOO .... #endif)的伸缩性往往会随着复杂性的增加而恶化。如果有大量的struct成员,则所有需要的编译器开关都将使程序变得更糟,无法维护。

有一个众所周知的设计模式,称为“ X宏”,可用于将程序中的维护集中到一个位置,并允许在编译时迭代涉及的所有项目。它们也使代码难以阅读,因此它们是最后的选择。但是它们有点实际标准,并且它们的丑陋程度并不会随着复杂性而扩展,因此它们比某些编译器转换疯狂更为可取。它是这样的:

#define INSTANCE_LIST \
/*  name, type */     \  
  X(name, bool)       \
  X(val1, bool)       \
  X(val2, bool)       \

typedef struct
{
  #define X(name, type) type name;
    INSTANCE_LIST
  #undef X
} instance_t;

此代码经过预处理:

typedef struct
{
  bool name;
  bool val1;
  bool val2;
} instance_t;

唯一需要维护的部分是“ INSTANCE_LIST”。通过注释掉列表中的一行,该结构成员将消失。这意味着所有使用该结构的代码都必须相应地使用相同的列表。例如,让我们在同一示例中添加代码,该代码列出每个成员的init值,然后进行设置:

#include <stdbool.h>
#include <stdio.h>

#define INSTANCE_LIST       \
/*  name, type, init */     \  
  X(name, bool, true)       \
  X(val1, bool, false)      \
  X(val2, bool, false)      \

typedef struct
{
  #define X(name, type, init) type name;
    INSTANCE_LIST
  #undef X
} instance_t;

int main (void)
{
  instance_t inst;

  #define X(name, type, init) inst.name = init;
    INSTANCE_LIST
  #undef X

  printf("%d ", inst.name);
  printf("%d ", inst.val1);
  printf("%d ", inst.val2);
}

非常灵活且可维护-您可以轻松添加更多的结构成员,而无需更改列表以外的任何其他宏。但是,如上所述,不利的一面是该代码看起来很神秘,尤其是对于那些不习惯这种设计模式的人。