Clang и LLVM решили вашу проблему, создав таблицы, содержащие их объекты, используя комбинацию генерации кода и хитрости препроцессора.
Вы можете пропустить любой шаг, в зависимости от ваших настроек.Например:
// records.inc
EXPAND_RECORD(Foo, "Foo", 4);
EXPAND_RECORD(Bar, "Bar", 18);
EXPAND_RECORD(Bar2, "Bar", 19);
Теперь вы можете сгенерировать enum:
// records.h
enum Record {
#define EXPAND_RECORD(Name, String, Value) Name,
#include "records.inc"
#undef EXPAND_RECORD
};
char const* getRecordName(Record r);
int getRecordValue(Record r);
// records.cpp
char const* getRecordName(Record r) {
switch(r) {
#define EXPAND_RECORD(Name, String, Value) case Name: return String;
#include "records.inc"
#undef EXPAND_RECORD
}
abort(); // unreachable, or you can return a "null" value
}
int getRecordValue(Record r) {
switch(r) {
#define EXPAND_RECORD(Name, String, Value) case Name: return Value;
#include "records.inc"
#undef EXPAND_RECORD
}
abort(); // unreachable, or you can return a "null" value
}
В Clang и LLVM используется фаза генерации кода для генерации .inc из более приятных файлов определений.
Это работает довольно хорошо ... но учтите, что любая модификация перечисления подразумевает полную перекомпиляцию.Возможно, вы захотите перейти к подходу «кодового набора», где перечисление используется внутренне, но никогда не просачивается наружу, и клиенту предоставляются стабильные значения (значения перечисления) (unsigned
), чтобы старые клиенты могли ссылаться нановые библиотеки без перекомпиляции: они будут ограничены использованием старого набора кодов, что не проблема, если он стабилен.