Инициализация массива с индексированием 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.