Инициализация массива с индексированием enum?

gc имеет очень хорошее расширение в 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++.

Каким будет предпочтительный легкий способ сделать то же самое на С++? Предпочтительно, что-то, не основанное на <map> и т.д., которые используют строковые ключи, скрытые индексы, тяжелые шаблоны или другие важные для процессора и памяти вещи?

Ответ 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;
    }
}

Приветствия и hth.,

Ответ 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");

который переносится и отвечает вашим требованиям, не имея особо "тяжелых шаблонов".

Если вы не используете С++ 11, вы все равно можете сделать это, макрос, который специализируется на шаблоне param, будет немного длиннее.


Модификация, используемая как int i = someinput(); cout << param<i>::t.name; legal:

#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 (?), чтобы определить неспециализированный шаблон этого имени.

Ответ 3

Дешевое, проницательное, мошенническое решение: определите переменную "values" в отдельном файле .c рядом со всеми .cpp файлами, определите enum и "extern values" в файле .h.