Инициализация индексированного массива? - PullRequest
11 голосов
/ 17 ноября 2011

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 ++? Желательно что-то, не основанное на

и подобных вещах, которые используют строковые ключи, скрытые индексы, тяжелые шаблоны или другие вещи, загружающие процессор и память?

Ответы [ 3 ]

14 голосов
/ 17 ноября 2011
#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.,

4 голосов
/ 17 ноября 2011

Я подозреваю, что это расширение существует именно потому, что не существует простого, переносимого способа достижения этого поведения. Вы можете эмулировать это, используя что-то вроде:

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

2 голосов
/ 17 ноября 2011

Дешевое, хитрое, обманчивое решение: определите переменную «values» в отдельном файле .c рядом со всеми файлами .cpp, определите enum и «внешние значения» в файле .h.

...