在 C 中用宏处理双变量 switch 语句
Handling double-variable switch statement with macro in C
假设我有两个指向两个结构 a
和 b
的指针。这两个结构各自包含一个枚举,我们称之为 x
。给定任何可能的 a
和 b
,我想根据它们的 x
枚举的值调用特定函数。
在我的案例中有趣的是我想调用的函数如下所示:
X0_to_X1();
X0_to_X2();
...
X1_to_X0();
...
etc
其中 X0
、X1
等是枚举 x
的可能值,这意味着对于每个值的每个组合都有 X_to_Y
个函数x
枚举。
明显的“天真”解决方案是一个相当大的 switch 语句(假设 x
有很多可能的值):
switch (a->x) {
case X0:
switch (b->x) {
case X1:
X0_to_X1();
break;
// ... and so on and so forth for every possible pair!
我第一次尝试更优雅地解决这个问题是实现一个宏,给定 x
的两个值,可以形成一个函数调用:
#define CALL_FUNCTION(x1, x2) x1 ## _to_ ## x2 ()
但是这不起作用,因为在我的代码中我永远无法在运行前知道 x
的 实际 值,所以它最终看起来像:
CALL_FUNCTION(a->x, b->x);
当然会转换为:
a->x_to_b->x();
这完全没有意义。
有没有办法更优雅地解决这个问题,还是我应该硬着头皮实现巨大的 switch 语句?
这个问题急需查找 table,您在其中存储指向各种函数的指针,它们由枚举值作为键。
如果您的 enum
值是连续的(并且没有两个枚举常量共享相同的值),那么您可以构建一个查找 table 作为一个简单的二维数组:
enum x_t { X0, X1, X2, ..., NUM_X };
void (*lookup[NUM_X][NUM_X])(void) = {
{ NULL, X0_to_X1, X0_to_X2, X0_to_X3, ... },
{ X1_to_X0, NULL, X1_to_X2, X1_to_X3, ... },
{ X2_to_X0, X2_to_X1, NULL, X2_to_X3, ... },
...
};
当您的 x
和 y
相同时,假设您没有“身份”功能。
然后,您通过索引到 table 来调用所需的函数,如下所示:
if ( x != y )
lookup[x][y]();
不,它不漂亮,但它胜过嵌套的 switch
语句。如果愿意,您可以将其隐藏在宏或其他函数调用后面。
如果您的枚举值不是连续的,那么这个特定的实现将不起作用 - 您必须使用列表或稀疏矩阵以不同的方式构建查找 table。但是虽然设置代码可能很繁琐,但它会大大简化调用方的逻辑。
假设我有两个指向两个结构 a
和 b
的指针。这两个结构各自包含一个枚举,我们称之为 x
。给定任何可能的 a
和 b
,我想根据它们的 x
枚举的值调用特定函数。
在我的案例中有趣的是我想调用的函数如下所示:
X0_to_X1();
X0_to_X2();
...
X1_to_X0();
...
etc
其中 X0
、X1
等是枚举 x
的可能值,这意味着对于每个值的每个组合都有 X_to_Y
个函数x
枚举。
明显的“天真”解决方案是一个相当大的 switch 语句(假设 x
有很多可能的值):
switch (a->x) {
case X0:
switch (b->x) {
case X1:
X0_to_X1();
break;
// ... and so on and so forth for every possible pair!
我第一次尝试更优雅地解决这个问题是实现一个宏,给定 x
的两个值,可以形成一个函数调用:
#define CALL_FUNCTION(x1, x2) x1 ## _to_ ## x2 ()
但是这不起作用,因为在我的代码中我永远无法在运行前知道 x
的 实际 值,所以它最终看起来像:
CALL_FUNCTION(a->x, b->x);
当然会转换为:
a->x_to_b->x();
这完全没有意义。
有没有办法更优雅地解决这个问题,还是我应该硬着头皮实现巨大的 switch 语句?
这个问题急需查找 table,您在其中存储指向各种函数的指针,它们由枚举值作为键。
如果您的 enum
值是连续的(并且没有两个枚举常量共享相同的值),那么您可以构建一个查找 table 作为一个简单的二维数组:
enum x_t { X0, X1, X2, ..., NUM_X };
void (*lookup[NUM_X][NUM_X])(void) = {
{ NULL, X0_to_X1, X0_to_X2, X0_to_X3, ... },
{ X1_to_X0, NULL, X1_to_X2, X1_to_X3, ... },
{ X2_to_X0, X2_to_X1, NULL, X2_to_X3, ... },
...
};
当您的 x
和 y
相同时,假设您没有“身份”功能。
然后,您通过索引到 table 来调用所需的函数,如下所示:
if ( x != y )
lookup[x][y]();
不,它不漂亮,但它胜过嵌套的 switch
语句。如果愿意,您可以将其隐藏在宏或其他函数调用后面。
如果您的枚举值不是连续的,那么这个特定的实现将不起作用 - 您必须使用列表或稀疏矩阵以不同的方式构建查找 table。但是虽然设置代码可能很繁琐,但它会大大简化调用方的逻辑。