初始化枚举索引的数组?

问题描述 投票:11回答:3

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和内存重的东西的东西?

c++ arrays gcc enums g++
3个回答
14
投票
#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;
    }
}

4
投票

我怀疑这个扩展的存在正是因为没有简单的、可移植的方法来实现这种行为。你可以使用类似:

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(?)来定义该名称的非专用模板。


2
投票

一个便宜的、偷偷摸摸的、作弊的解决方案:在所有.cpp文件旁边的一个单独的.c文件中定义 "值 "变量,在一个.h文件中定义枚举和 "外部值"。

© www.soinside.com 2019 - 2024. All rights reserved.