Требования:
Вот что я до сих пор придумал:
- test.hh -
// Generic mapper
//
// The idea here is to create a map between an integer and a string.
// By including it inside a class we prevent every module which
// includes this include file from creating their own instance.
//
struct Mapper_s
{
int Idx;
const char *pStr;
};
// Status
enum State_t
{
Running = 1,
Jumping = 6,
Singing = 12
};
struct State_s
{
static const Mapper_s *GetpMap(void)
{
static Mapper_s Map[] =
{
{ Running, "Running" },
{ Jumping, "Jumping" },
{ Singing, "Singing" },
{ 0, 0}
};
return Map;
};
};
- test.cc -
// This is a generic function
const char *MapEnum2Str(int Idx, const Mapper_s *pMap)
{
int i;
static const char UnknownStr[] = "Unknown";
for (i = 0; pMap[i].pStr != 0; i++)
{
if (Idx == pMap[i].Idx)
{
return pMap[i].pStr;
}
}
return UnknownStr;
}
int main()
{
cout << "State: " << MapEnum2Str(State, State_s::GetpMap()) << endl;
return 0;
}
Есть предложения, как это улучшить?
Мне кажется, что заголовочный файл выглядит немного загроможденным ...
Есть еще ограничения? Есть ли причина использовать State_s вместо одной только карты? Можно ли предположить, что карта определена таким образом, что Idx увеличивается?
Вы в основном просто пытаетесь выполнить интернирование строк, только вы хотите иметь возможность конкретно указать значения интернированных строк?


I can't do a direct mapping since one application might be feeding me values which are >out of range for my list of strings. A direct lookup into nothingness would be critical.
Поэтому добавьте количество объектов в массив, которые проверяют ваши методы доступа, прежде чем возвращать значение:
struct map {
const char *mapping[] = { "Running", "Jumping", "Singing" };
const int count = 3;
}
или если вы хотите автоматизировать
struct map {
map() {
for( count = 0; strlen( mapping[count] ); ++i )
}
const char *mapping[] = { "Running", "Jumping", "Singing", "" };
int count;
}
Извините, но я забыл упомянуть, что индексы могут быть не в последовательном порядке, т.е. в перечислениях могут быть дыры. Вот почему мне нужно выполнить ручной поиск.
Как насчет Простой способ использовать переменные перечислимого типа как строку в C?
В этом стиле перечисления и строки не только содержатся в одном файле, они также содержатся в одном месте. Вы можете легко расширить фабрику, чтобы принимать больше «столбцов» в SOME_ENUM, в вашем случае вы можете захотеть, чтобы строка не генерировалась из имени, а предоставлялась явно.
Самое быстрое выполнение: построение хеш-таблицы. Поскольку вы знаете индексы заранее, вы даже можете построить идеальная хеш-таблица.
Самый простой в программировании: используйте массивный оператор switch и надейтесь, что компилятор знает, как оптимизировать для целых чисел, не являющихся смежными. По крайней мере, строки будут жить в сегменте .text самого исполняемого файла, поэтому вам не нужно ничего создавать:
// Status
enum State_t
{
Running = 1,
Jumping = 6,
Singing = 12
};
const char *StateToString(State_t state)
{
switch(state)
{
case Running: return "Running";
case Jumping: return "Jumping";
case Singing: return "Singing";
default: return "ERROR";
}
}
Вы можете скрыть все это внутри макросов (как предлагает ссылка Suma), чтобы это не было WTF.
Вот на чем я остановился. Используя эту технику, все, что вам нужно сделать, это включить файл заголовка. Вы создадите только то, что используете. Вы также можете сохранить идеальную хеш-таблицу вместо просто Idx & pStr. Этот подход не работает в C.
файл: e2str.hh
struct Mapper_s
{
int Idx;
const char *pStr;
};
#define ENUM2STR_BEGIN(x) struct x { static const Mapper_s *GetpMap(void) { static const Enum2StrMap_s Map[] =
#define ENUM2STR_END return Map; }; }
const char *MapEnum2Str(int Idx, const Mapper_s *pMap);
файл: mapper.hh
#include "e2str.hh"
ENUM2STR_BEGIN(State_s)
{
{ Running, "Running" },
{ Singing, "Singing" },
{ Jumping, "Jumping" },
{ 0, 0}
};
ENUM2STR_END;
файл: test.cc
#include "mapper.hh"
int main()
{
cout << "State: " << MapEnum2Str(State, State_s::GetpMap()) << endl;
return 0;
}
Для завершения цикла должно быть pMap [i] .IdX! = INT_MAX; Если массив хранится последовательно, вы можете напрямую ссылаться на состояние <= KNOWN_STATE? pMap [состояние-1]: «Неизвестно» при условии, что KNOWN_STATE = 4;