我有一堆通过回调设置的全局变量。我一次收到其中一个通知。我想保持单一功能来更新这个状态。这是我正在做的事情的版本。
typedef struct {
int g_var_a;
char g_var_b;
double g_var_c;
long g_var_d;
} global_state_t;
typedef union {
int g_var_a;
char g_var_b;
double g_var_c;
long g_var_d;
} global_change_t;
typedef enum {
VAR_A;
VAR_B;
VAR_C;
VAR_D;
} global_change_type_t;
global_state_t gs = {0};
void udpate_global_state(global_change_t *c, global_change_type_t type) {
switch (type) {
case VAR_A: {
gs.g_val_a = c->g_var_a;
break;
}
...
....
}
}
这称为:
callback() {
...
global_change_t c = {.g_var_a = 1234};
update_global_state(&c, VAR_A);
}
但对我来说这看起来很糟糕。有三个结构:struct,union,enum,所有这些都必须同步。难道没有更好的方法吗?
答案 0 :(得分:1)
在C中没有优雅的方法来定义运行时变体。
这两种方法包括:
struct
包含具有实际值的联合和一些基于实际类型分派的机制。在简单的情况下,这可以是enum
标记,在更复杂的情况下,会有一些函数指针存储对value_is()
和get_value_as()
方法的引用(后面的方法主要用于动态语言)口译)。struct
和union
标记的enum
,但可能会导致更多可维护和可读的程序牺牲一些(不是那么大的)性能损失。答案 1 :(得分:1)
首先我要注意,通过使每个结构字段类型只有一个字段,而不是每个字段一个字段,您可以降低维护风险。您还可以使用宏来完全消除冗余。如果你有兴趣,我可以进一步解释。
您还可以使用函数从数据中抽象出来。不幸的是,要给它们所有相同的签名,你需要一个你使用的联合或无效指针。我会试试后者。
typedef struct {
int g_var_a;
char g_var_b;
double g_var_c;
long g_var_d;
} GLOBAL_STATE;
GLOBAL_STATE gs[1];
typedef void (*UPDATER)(void*);
void a_updater(void *val) { gs->g_var_a = *(int*)val; }
void b_updater(void *val) { gs->g_var_b = *(char*)val; }
void c_updater(void *val) { gs->g_var_c = *(double*)val; }
void d_updater(void *val) { gs->g_var_d = *(long*)val; }
void udpate_global_state(UPDATER updater, void *val) {
updater(val);
}
注意:您不应使用以_t
结尾的类型,因为这些类型是由C标准为系统标题保留的。
现在你可以说
{
int i[1] = { 3 };
char c[1] = { 'a' };
double d[1] = { 1.234 };
long g[1] = { 123456 };
update_global_state(a_updater, i);
update_global_state(b_updater, c);
update_global_state(c_updater, d);
update_global_state(d_updater, g);
}
单元素阵列技巧纯粹是美学的。如果您愿意,可以删除它并在更新调用中使用&
。两种方式都产生相同的代码。
当然这不是类型安全的,但你的解决方案也不是。它确实减少了必须同步维护的代码结构。