单个处理程序,用于设置不同类型的值

时间:2013-12-05 01:43:45

标签: c data-structures coding-style

我有一堆通过回调设置的全局变量。我一次收到其中一个通知。我想保持单一功能来更新这个状态。这是我正在做的事情的版本。

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,所有这些都必须同步。难道没有更好的方法吗?

2 个答案:

答案 0 :(得分:1)

在C中没有优雅的方法来定义运行时变体。

这两种方法包括:

  1. struct包含具有实际值的联合和一些基于实际类型分派的机制。在简单的情况下,这可以是enum标记,在更复杂的情况下,会有一些函数指针存储对value_is()get_value_as()方法的引用(后面的方法主要用于动态语言)口译)。
  2. 通用值类型(通常是字符串),可以编码不同的变量值。接收函数然后将进行一些简单的解析以获取数据(这基本上相当于带有structunion标记的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);
}

单元素阵列技巧纯粹是美学的。如果您愿意,可以删除它并在更新调用中使用&。两种方式都产生相同的代码。

当然这不是类型安全的,但你的解决方案也不是。它确实减少了必须同步维护的代码结构。