C MacroMagic - 结构定义

C MacroMagic - Struct Definition

我正在寻找定义结构的解决方案,用户可以在其中 enable/disable 结构成员,如示例(伪代码):

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

我不确定这有多大用处,但以下内容应该可以满足您的要求:

#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 等定义。然后你可以使用 #if 指令进行正常的 struct 定义:

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

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

那么您还可以在任何访问 name 成员的库代码周围放置 #if 指令。

鉴于需要在编译时以不同方式生成结构,在某些情况下,您将面临这样一个问题,即所有使用该结构的代码都需要相应地进行修改。编译器开关 (#ifdef FOO .... #endif) 往往会随着复杂性的增加而严重扩展。如果有大量结构成员,所有需要的编译器开关都会使程序变得可怕、无法维护。

有一种众所周知的设计模式 "X macros",可用于将程序中的维护集中到一个地方,并允许对所有相关项目进行编译时迭代。它们也使代码难以阅读,因此它们是最后的手段。但它们有点事实上的标准,而且它们的丑陋程度不会随着复杂性而增加,因此它们比某些编译器开关疯狂更受欢迎。它是这样的:

#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"。通过注释掉列表中的一行,该结构成员将消失。这意味着所有使用该结构的代码都必须相应地使用相同的列表。例如,让我们将代码添加到同一示例,列出每个成员的初始值,然后设置它们:

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

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