gcc在C语言中有一个非常好的扩展,允许你用enum作为键在数组中保存数据。
enum keys
{
key_alpha = 0,
key_beta = 1,
key_gamma = 2
};
ValType values =
{
[ key_alpha ] = { 0x03b1,"alpha" },
[ key_gamma ] = { 0x03b3,"gamma" },
[ key_beta ] = { 0x03b2,"beta" }
};
这很好,因为如果列表要改变,添加或删除一行并不会破坏赋值,哪个键对应哪个值是显而易见的,结果是简单的代码,与普通的标准数组初始化没有区别。
遗憾的是,这种扩展在g++中无法实现。
在C++中,有什么轻量级的方法可以做同样的事情?最好是不基于<map>等使用字符串键、隐藏索引、重模板或其他CPU和内存重的东西的东西?
#include <iostream>
#define KEYS_DEF \
KEY_DEF( alpha, 0x03b1, "alpha" ), \
KEY_DEF( beta, 0x03b2, "beta" ), \
KEY_DEF( gamma, 0x03b3, "gamma" )
#define KEY_DEF( identifier, id, name ) identifier
enum keys { KEYS_DEF };
#undef KEY_DEF
#define KEY_DEF( identifier, id, name ) { id, name }
struct ValType { int id; char const* name; };
ValType const values[] = { KEYS_DEF };
int main()
{
using namespace std;
for( int i = alpha; i <= gamma; ++i )
{
cout << values[i].name << endl;
}
}
我怀疑这个扩展的存在正是因为没有简单的、可移植的方法来实现这种行为。你可以使用类似:
enum keys
{
key_alpha = 0,
key_beta = 1,
key_gamma = 2
};
struct ValType {
int v;
const char *name;
};
template <int key>
struct param;
#define SETPARAM(key,value1,value2) \
template <> \
struct param< (key) > { \
static constexpr ValType t {(value1),(value2)}; \
}
SETPARAM(key_alpha, 0x03b1,"alpha");
SETPARAM(key_gamma, 0x03b3,"gamma");
SETPARAM(key_beta, 0x03b2,"beta");
这样的方法既可移植,又能满足你的要求,同时又不会有特别 "重的模板"。
如果你没有使用C++11,你仍然可以做到这一点,专门的宏是 param
不过模板会变得稍微长一些。
修改为使用像 int i = someinput(); cout << param<i>::t.name;
合法的。
#include <cassert>
enum keys
{
key_alpha = 0,
key_beta = 1,
key_gamma = 2
};
struct ValType {
int v;
const char *name;
};
template <int key>
struct param {
enum { defined = false };
static constexpr ValType t {0, 0};
};
template <int key>
constexpr ValType param<key>::t;
static const int MAXPARAM=255;
#define SETPARAM(key,value1,value2) \
template <> \
struct param< (key) > { \
static_assert(key <= MAXPARAM, "key too big"); \
enum { defined = true }; \
static constexpr ValType t {(value1),(value2)}; \
}; \
constexpr ValType param<(key)>::t
template <int C=0>
struct get_helper {
static const ValType& get(int i) {
return i==0 ? (check(), param<C>::t) : get_helper<C+1>::get(i-1);
}
private:
static void check() {
assert(param<C>::defined);
}
};
template <>
struct get_helper<MAXPARAM> {
static const ValType& get(int) {
assert(false);
}
};
const ValType& GETPARAM(int key) {
return get_helper<>::get(key);
}
诀窍是实例化 get_helper
并通过调用递归一个标志,这个标志可以用来保证索引的有效性。你可以增加 MAXPARAM
如果需要,但会使编译速度变慢。
例子的用法还是很简单的。
#include "enumidx.hh"
#include <iostream>
SETPARAM(key_alpha, 0x03b1,"alpha");
SETPARAM(key_gamma, 0x03b3,"gamma");
SETPARAM(key_beta, 0x03b2,"beta");
int main() {
int key = key_beta;
const ValType& v = GETPARAM(key);
std::cout << v.name << std::endl;
}
要想在任何一个程序中拥有多个这样的程序,你可以使用匿名的命名空间,或者将基础程序的名字改为 "匿名"。struct
(param
在本例中)一个宏参数,并添加另一个宏。STARTPARAM
(?)来定义该名称的非专用模板。
一个便宜的、偷偷摸摸的、作弊的解决方案:在所有.cpp文件旁边的一个单独的.c文件中定义 "值 "变量,在一个.h文件中定义枚举和 "外部值"。