【问题标题】:Initializing enum-indexed array?初始化枚举索引数组?
【发布时间】:2011-12-31 07:29:06
【问题描述】:

gcc 在 C 中有一个非常好的扩展,它允许您使用枚举作为键将数据保存在数组中:

 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++ 中做同样事情的首选轻量级方法是什么?最好不是基于 之类的东西,使用字符串键、隐藏索引、繁重的模板或其他 CPU 和内存繁重的东西?

【问题讨论】:

  • C++11 在解决方案中是否可以接受?
  • 它被称为designated initializers,在 C99 中可用,编译器可以将其作为扩展提供。
  • (顺便说一句,您的示例代码缺少分号)
  • @Nawaz:gcc 默认开启此功能,GCC 扩展页面专门将其列为 g++ 中不存在的功能。
  • 我们能否将问题改写为:“在 C++ 中是否可以通过以任意顺序指定元素来初始化数组?”

标签: c++ arrays gcc enums g++


【解决方案1】:
#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;
    }
}

【讨论】:

    【解决方案2】:

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

    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 &lt;&lt; param&lt;i&gt;::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( ?) 定义该名称的非专业模板。

    【讨论】:

    • 这不允许动态查找,所以我认为它不是一个有效的替代品。
    • @BenVoigt - 你可能就在那儿,我没想到。你的意思是例如int i = someinput(); cout &lt;&lt; param&lt;i&gt;::t.name; 是非法的?
    • 不幸的是,这对我没有好处。它通常用于嵌套在函数深处,键作为参数传递。
    • @SF - 我可以解决这个问题,但它更进一步进入了“重型模板”的领域。我认为您不会得到基本上不仅仅是std::map 或“重模板”的答案
    • @SF - 答案已更新。我也可以修改它以使其更通用,但事情开始变得更加复杂!
    【解决方案3】:

    一种廉价、偷偷摸摸、欺骗性的解决方案:在所有 .cpp 文件旁边的单独 .c 文件中定义“值”变量,在 .h 文件中定义枚举和“外部值”。

    【讨论】:

    • Android 初始化文件解析器中有一个很好的例子。 header 中的值和 c 中的变量 file
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-21
    • 1970-01-01
    相关资源
    最近更新 更多