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);
}
非常灵活且易于维护 - 您可以轻松添加更多结构成员,而无需更改列表之外的任何其他宏。但如前所述,缺点是代码看起来很神秘,尤其是对于那些不习惯这种设计模式的人。
我正在寻找定义结构的解决方案,用户可以在其中 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);
}
非常灵活且易于维护 - 您可以轻松添加更多结构成员,而无需更改列表之外的任何其他宏。但如前所述,缺点是代码看起来很神秘,尤其是对于那些不习惯这种设计模式的人。