有没有办法反转#define指令?
在以下示例中
#define ZERO 0
#define ONE 1
#define TWO 2
#define THREE 3
是否可以从整数值2中检索两个?
此示例来自C代码,但如果需要,我可以使用一些C ++代码。我的目标是能够分解这种形式的一些虚假的switch-case循环:
switch(num)
{
case ZERO:
return std::to_string(foo.V_ZERO);
case ONE:
return std::to_string(foo.V_ONE);
case TWO:
return std::to_string(foo.V_TWO);
case THREE:
return std::to_string(foo.V_THREE);
}
其中foo是这样的结构的实例:
struct Foo
{
union Val
{
int V_ZERO;
int V_ONE;
double V_TWO; // nonsense: just to say that types are not the same
int V_THREE;
};
};
我的约束如下:
我有一些简化代码的想法,但我想知道是否有一种非常着名的方法,特别是通过一些模板或预处理器指令。
编辑:添加std :: to_string的用法,说我不知道如何转换或处理联合中的多个类型。
答案 0 :(得分:3)
如果要自动执行某些过程,可以选择使用xmacros。他们是hacky,但根据你的代码大小可能会使你的同事们的愤怒变得更容易维护(如增加新条目):
定义xmacro列表:
#define XLIST(xmacro) \
xmacro(ZERO, 0) \
xmacro(ONE, 1) \
xmacro(TWO, 2) \
xmacro(THREE, 3) \
然后在想要遍历所有项目时使用它:
// create an enum containing all items
#define xmacro(name, value) name,
enum Item
{
XLIST(xmacro)
};
#undef xmacro
// get the number of items
#define xmacro(name, value) +1
const int NumberOfItems = 0 XLIST(xmacro);
#undef xmacro
// enum -> value
int itemToValue(enum Item item)
{
switch (item)
{
// create a mapping from enum to x
#define xmacro(name, value) case name: return value;
XLIST(xmacro)
#undef xmacro
}
return -1;
}
// get enum name
const char * getItemName(enum Item item)
{
switch (item)
{
// create a mapping from enum to x
#define xmacro(name, value) case name: return #name;
XLIST(xmacro)
#undef xmacro
}
return NULL;
}
这将被预处理为类似:
enum Item
{
ZERO,
ONE,
TWO,
THREE,
};
const int NumberOfItems = 0 +1 +1 +1 +1; // == 4
int itemToValue(enum Item item)
{
switch (item)
{
case ZERO: return 0;
case ONE: return 1;
case TWO: return 2;
case THREE: return 3;
}
return -1;
}
const char * getItemName(enum Item item)
{
switch (item)
{
case ZERO: return "ZERO";
case ONE: return "ONE";
case TWO: return "TWO";
case THREE: return "THREE";
}
return NULL;
}
您可以创建几乎任何您想要的映射,即对于您的结构,您将使用类似于@ Jean-François所写的内容:
// get struct value by item type
double getValueByName(enum Item item, struct Foo values)
{
switch (item)
{
// create a mapping from enum to x
#define xmacro(name, value) case name: return values.V_##name;
XLIST(xmacro)
#undef xmacro
}
return -1;
}
答案 1 :(得分:1)
不,那是不可能的。
预处理器#define
在预处理阶段由文本替换为其定义。实际的编译器永远不会看到符号。在运行时,您认为TWO
是什么意思?它已经被整个文字2
所取代,所以没有变化。
const int two_define = TWO;
const int two_literal = 2;
都会将整数值2
放在相应的变量中,没有任何魔法气味以某种方式将TWO
与2
区分开来。编译器将看到const int two_define = 2;
,因为预处理器符号将消失。
另外:假设您同时返回int
和double
s,函数的实际返回类型是什么?这意味着它是double
。
答案 2 :(得分:1)
在一般情况下,您必须采用类似“X宏”的内容,如另一个答案中所建议的那样。但是,当其他一切都失败时,这是最后的手段。如果数字完全是任意的,你就必须去那里。
然而,在这种特定情况下,数字是相邻的并且从零开始。这需要enum
与查找表相结合。实现它的标准方法是这样的:
#include <stdio.h>
typedef enum
{
ZERO,
ONE,
TWO,
THREE,
SUPPORTED_NUMBERS
} number_t;
const char* STR_NUMBER [] =
{
"ZERO",
"ONE",
"TWO",
"THREE",
};
_Static_assert((sizeof STR_NUMBER / sizeof *STR_NUMBER) == SUPPORTED_NUMBERS,
"Error: enum does not correspond to look-up table.");
int main (void)
{
for(number_t i=0; i<SUPPORTED_NUMBERS; i++)
{
printf("%d %s\n", i, STR_NUMBER[i]);
}
}
答案 3 :(得分:0)
对于这两个问题:
有没有办法反转#define指令?
是否可以从整数值2中检索两个?
也许数组映射结构适合您:
#define ZERO 0
#define ONE 1
#define TWO 2
#define THREE 3
typedef struct
{
int number;
const char *name;
} name_map_t;
#define MAP_NAME_STR(id) { id, #id },
static const name_map_t name_map_table[] =
{
MAP_NAME_STR( ZERO )
MAP_NAME_STR( ONE )
MAP_NAME_STR( TWO )
MAP_NAME_STR( THREE )
};
然后只循环name_map_table
数组以找到想要的对应物。
答案 4 :(得分:0)
我确定了你的问题,当我面对这种 Enumeration 模式时,我会告诉你我做了什么:
假设我有一个值的枚举,我要打印到switch
,以表示数组索引等。
首先,我编写了一个包含文件,该文件将支持我将对此枚举类型执行的各种用法:
enum(INITIAL_STATE, 0, "This is the initial state")
enum(FLAG_READ, 1, "We have read the flag symbol")
...
然后我使用不同的宏定义来扩展寄存器的数据集,如下所示:
struct enum MyEnum {
#define enum(val,ix, string) val,
#include "myenumdef.i"
#undef enum
};
/* then, later in the same file ... */
char *MyEnumStrings[] = {
#define enum(val,ix, string) string,
#include "myenumdef.i"
#undef enum
};
/* .... */
char *MyEnumNames[] = {
#define enum(val, ix, string) #val, /* the string equivalent of enum names */
#include "myenumdef.i"
#undef enum
};
/* and more complex forms... like */
struct myEnumDesc {
int e_val;
int e_ix;
char *e_name;
char *e_desc;
} enum_table[] = {
#define enum(val,ix,string) val, ix, #val, string,
#include "myenumdef.i"
#undef enum
}; /* enum_table */
/* ... even, when I want to switch on them */
switch(val) {
#define enum(val,ix,string) case VAL_##val: return string"("#val"="#ix")";
#include "myenumdef.i"
#undef enum
default: return "invalid val";
} /* switch */
只需准备一个上面的例子,然后通过C预处理器运行它,看看最终的C代码是怎样的。
你可以多次我需要这样做(即使是在一个文件中),我只需要改变宏enum
的方式(在这种情况下,你甚至可以有几个宏名来定义和使用)在包含数据文件之前,它们构造更复杂的数据依赖性。如果你想在枚举中添加一个常量,你只需将它添加到.i
文件中,按上面所示进行编码,一切都将适应变化。
你问过C-only解决方案,所以我给出了关于如何处理同一组项目的不同定义的更多近似方式。