我有一些常量值和数组定义它们的标签及其哈希码。例如,
#define LABEL_A 0 //or const int LABEL_A = 0;
#define LABEL_B 1
#define LABEL_C 2
#define LABEL_D 3
const char *VALUE[] = {"LABEL_A", "LABEL_B", "LABEL_C", "LABEL_D"};
const int VALUE_HASH[] = {67490, 67491, 67493, 67459);
在运行时,这些标签可以按任何顺序排列,需要进行相应的解析。我正在使用开关盒来达到这个目的。 此代码在编译时生成错误"需要常量表达式。
function(const char* LabelAtRuntime){
int i = getHashCode(LabelAtRuntime);
switch(i){
case VALUE_HASH[LABEL_A]: //line giving compile time error
break;
default:
break;
}
但是,当我提供实际常数时,它会起作用。 此代码效果很好。
function(const char* LabelAtRuntime){
int i = getHashCode(LabelAtRuntime);
switch(i){
case 67490: //line not giving compile time error
break;
default:
break;
}
我以这种方式使用常量来提供更好的代码语义,可读性和可重用性。请不要提供基于if-else
的解决方案。在上面的例子中,只有4个标签,但实际上可能有100个。
答案 0 :(得分:11)
在C ++中,这会编译:
#include <stdio.h>
#include <stdlib.h>
constexpr int x[] = { 42, 43 };
int main(int argc, char **argv)
{
switch(atoi(argv[1]))
{
case x[0]: puts("forty_two");
break;
case x[1]: puts("forty_three");
}
return 0;
}
所以数组上的constexpr
似乎是现代C ++中的解决方案。
(注意:问题最初标记为C ++和C)
如果你想保留阵列,那么在C中是不可能的。切换案例需要integer constant,但是一旦在变量中放入一个整数常量,它就会变成一个运行时实体(即使它被声明为const)。 你可以做的是用一堆直接定义替换内存中的数组,并且可能有一个使用其他宏查找宏的宏(如果你想保留你的代码形式):
#define LABEL_A 0
#define LABEL_B 1
#define LABEL_C 2
#define LABEL_D 2
#define VALUE_HASH__0 67490
#define VALUE_HASH__2 67491
#define VALUE_HASH__3 67491
#define VALUE_HASH__4 64759
//append what Index expands to to VALUE_HASH__
#define HASH_LOOKUP(Index) MC_cat(VALUE_HASH__,Index)
#define MC_cat_(X,Y) X##Y
#define MC_cat(X,Y) MC_cat_(X,Y)
function(const char* LabelAtRuntime){
int i = getHashCode(LabelAtRuntime);
switch(i){
case HASH_LOOKUP(LABEL_A)
break;
default:
break;
}
答案 1 :(得分:5)
错误的原因只是C不认为const int LABEL_A=0;
是编译时常量。不幸的是,语言是如何定义的。可以使用#define LABEL_A 0
来解决它。
第三种方法是使用枚举,这可能更好,因为它们可用于将所有数据绑定在一起,并在维护期间提供更多的数据完整性:
typedef enum
{
LABEL_A,
LABEL_B,
LABEL_C,
LABEL_D,
LABELS_N
} label_index_t;
typedef void func_t (void);
typedef struct
{
const char* str;
int hash;
func_t* func;
} value_t;
...
const value_t VALUE [] =
{
[LABEL_A] = { .str = "LABEL_A", .hash = 67490, .func = a_func },
[LABEL_B] = { .str = "LABEL_B", .hash = 67491, .func = b_func },
[LABEL_C] = { .str = "LABEL_C", .hash = 67493, .func = c_func },
[LABEL_D] = { .str = "LABEL_D", .hash = 67459, .func = d_func },
};
_Static_assert(sizeof VALUE / sizeof *VALUE == LABELS_N,
"Size of VALUE does not match label_t.");
...
// instead of switch(n):
VALUE[n].func();
答案 2 :(得分:4)
切换案例需要在编译时知道值。在您的数组值的情况下,直到运行时才知道这些值。
如果您使用的是c ++ 11,则可以使用constexpr
强制编译器在编译时评估数组值。下面的代码工作正常。
constexpr int VALUE_HASH[] = {67490, 67491, 67493, 67459};
int i = getHashCode(LabelAtRuntime);
switch(i) {
case VALUE_HASH[LABEL_A]:
break;
default:
break;
}
答案 3 :(得分:2)
我不知道你真正想要做什么。但是当我不得不在C中实现菜单UI时,我这样做了:
// Typedef for a menu item's logic function (callback):
typedef void (*menu_item_cb_t)(void*)
// Struct defining a menu item:
typedef struct menu_item {
const char* label;
const int hashValue;
const menu_item_cb_t callback;
const void* callback_arg;
} menu_item_t;
// Callback for menu item "Do X":
void menu_do_x( void* arg ) {
// ...
}
// Definition of menu item "Do X":
const menu_item_t menu_item_x = {
"Do X",
12345,
&menu_do_x,
NULL // Don't need it to do x
}
// All menu items go into one array:
const menu_item_t* MENU[] = { &menu_item_x, ...};
#define MENU_ITEM_CNT xxx
然后您可以对所选项目采取行动,如:
void menuItemSelected( const char* label ) {
const int hash = getHashCode(label);
for ( int i = 0; i < MENU_ITEM_CNT; i++ ) {
const menu_item_t* const mi = MENU[i];
if ( hash == mi->hash ) {
mi->callback( mi->callback_arg );
break;
}
}
}
这种方法当然可以改变,但我希望你能得到这个想法。它基本上只用于定义具有特定属性的项目(&#34;标签&#34;,&#34;哈希&#34;以及其他任何内容),并直接将它们与为此实现相应操作的函数相关联。项目
答案 4 :(得分:1)
如果VALUE_HASH
仅用于获取开关常量,为什么不用跳转表替换它?
注意,以下所有内容均未经过测试或编译。可能存在语法错误。
首先为表中的fucntions定义一个类型:
typedef void (*Callback)(/* parameters you need */);
然后你需要你的实际功能
void labelAProcessing(/* the parameters as per the typedef */)
{
/// processing for label A
}
// etc
然后你的表
Callback valueCallbacks[] = { labelAProcessing, labelBProcessing, ... };
您的代码变为
int i = getHashCode(LabelAtRuntime);
valueCallbacks[i](/* arguments */);
我不能过分强调i
需要进行验证以确保它是valueCallbacks
数组的有效索引,否则,您可能会将随机数称为函数。
答案 5 :(得分:1)
操作数是常量是不够的。在编译时知道它们是不够的(无论tbat意味着什么,C标准都没有在这些术语中说话)。案例标签必须是整数常量表达式。
整数常量表达式由C标准严格定义。粗略地说,整数常量表达式必须由整数常量(也是枚举器,字符常量等)构建,并且不能包含数组或指针,即使它们是常量也是如此。有关可访问但最简单的解释,请参阅例如this