Но тут приходиться вручную создавать объекты Command, что неудобно.
Есть ли решение лучше ?
Re: Как бы сделать такие enum 'ы..
От:
Аноним
Дата:
31.01.08 13:25
Оценка:
Это стандартная проблема на С++ проектах, как конвертировать enum'ы с строки и обратно.
Один из варинтов примерно такой:
class CommandKind
{
public:
enum Id
{
APP_INIT,
APP_CLOSE
};
std::string toString( Id id )
{
switch( id )
{
case APP_INIT: return std::string("APP_INIT");
case APP_CLOSE: return std::string("APP_CLOSE");
}
return std::string("Undefind id")
}
Id fromString( const std::string& idAsString );
}
Какое бы ты решение не выбрал, всегда придется писать какой-то код.
Но поскольку он всегда одинаков, то это дело можно очень хорошо автоматизировать
и быстро налабать какой-нибудь скриптик, которые подобные классы генерит автоматически...
... А>Как-бы так ассоциировать с кодом команды строчку текста ? Мне приходит в голову
Например, написать функцию, которая будет по коду enum-а возвращать его текстовое описание,
в функции связывать текст с кодом через switch-case или массив описаний (если коды от нуля и
больше, и без разрывов).
... А>Есть ли решение лучше ?
Например, так http://lists.pilot-link.org/pipermail/pilot-link-devel/2005-April/001344.html
Здравствуйте, NailS, Вы писали:
NS>Например, написать функцию, которая будет по коду enum-а возвращать его текстовое описание, NS>в функции связывать текст с кодом через switch-case или массив описаний (если коды от нуля и NS>больше, и без разрывов).
Муторно
А>>Есть ли решение лучше ? NS>Например, так NS>http://lists.pilot-link.org/pipermail/pilot-link-devel/2005-April/001344.html
С лёту не понял, нужно разобраться..
Здравствуйте, AndryBlack, Вы писали:
AB>Здравствуйте, Аноним, Вы писали: AB>если Ваша "религия" позволяет активно использовать препроцессор, то например так: AB>
Здравствуйте, shvonder, Вы писали:
S>Думал об этом но это ещё муторнее и чревать ошибками.
На С++ ничего принципиально другого не сделаешь...
Ну нету в С++ метаданных, рефлекшена и других подобных вещей.
А ошибками это не чревато, если генерацию таких классов автоматизировать
на основе внешнего описания.
Как только у тебя будут все скрипты и все будет автоматизировано,
то разницы не будет, где редактировать твой enum, в каком-то h файле,
или в каком-то внешнем txt/xml файле
Ну а если все равно муторно, то ты профессию не ту выбрал, ну или как минимум язык.
Пиши на шарпе или управляемом С++. Там это все на шару будет
S>Здравствуйте, AndryBlack, Вы писали:
AB>>Здравствуйте, Аноним, Вы писали: AB>>если Ваша "религия" позволяет активно использовать препроцессор, то например так:
Поразмыслив, принял ваш вариант. Мало нажатий клавиш , очень просто (в отличие от предложения NailS'а) и для моих целей хватит.
Хотя, конечно, варварски.
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, shvonder, Вы писали:
S>>Думал об этом но это ещё муторнее и чревать ошибками.
А>На С++ ничего принципиально другого не сделаешь... А>Ну нету в С++ метаданных, рефлекшена и других подобных вещей.
А>А ошибками это не чревато, если генерацию таких классов автоматизировать А>на основе внешнего описания. А>Как только у тебя будут все скрипты и все будет автоматизировано, А>то разницы не будет, где редактировать твой enum, в каком-то h файле, А>или в каком-то внешнем txt/xml файле
А>Ну а если все равно муторно, то ты профессию не ту выбрал, ну или как минимум язык. А>Пиши на шарпе или управляемом С++. Там это все на шару будет
Кстати писать
switch( id )
{
case APP_INIT: return std::string("APP_INIT");
case APP_CLOSE: return std::string("APP_CLOSE");
}
не так уж и плохо, как кажется (т.е. отдельно писать константу и её целочисленное значение, а не использовать метаданные).
Я как-то писал для себя программу на C# и задумал локализовать её.
Мне надо было вывести названия цветов — по английски не проблема, получить список всех доступных цветов легко можно, но что делать с русским языком?
Я не смог найти хороший способ вывести список всех доступных цветов на русском языке кроме как самому вбить их.
А для своих enum тем более потребуется вводить.
Здравствуйте, shvonder, Вы писали:
AB>>>если Ваша "религия" позволяет активно использовать препроцессор, то например так: S>Поразмыслив, принял ваш вариант. Мало нажатий клавиш , очень просто (в отличие от предложения NailS'а) и для моих целей хватит. S>Хотя, конечно, варварски.
А вот так пойдёт?
#include"boost/preprocessor.hpp"//Declaring our enum#define ENUM_SEQ_COMMAND_KIND\
(((APP_INIT) (1) ) ("APP_INIT_BLA_BLA"))\
(((APP_CLOSE) (10)))\
(((APP_ABRACADABRA) ) ("Strange message"))\
(((APP_TERMINATE) ))
//Decryption:
//<Enum> -> <EnumIntValue> -> <EnumStringValue>
//APP_INIT -> 1 -> "APP_INIT_BLA_BLA"
//APP_CLOSE -> 10 -> "APP_CLOSE"
//APP_ABRACADABRA -> 11 -> "Strange message"
//APP_TERMINATE -> 12 -> "APP_TERMINATE"
//
//Format:
//(((<Enum>) [(<EnumIntValue>)]) [(<EnumStringValue>)])
// Where,
// [] - optional sequence argument.
//
//Features:
// 1. Both <EnumIntValue> and <EnumStringValue> - are optional.
// 2. If <EnumIntValue> in first enum doesn't typed, then it would be - 0 (zero).
// 3. If <EnumIntValue> in non first enum doesn't typed, then it would be previous enum value plus 1 (<EnumIntValue>+1).
//
// Generate single enumeration#define _GENERATE_ENUM_FROM_SEQ(z,n,enums_seq)\
BOOST_PP_IF(n,BOOST_PP_COMMA,BOOST_PP_EMPTY)() BOOST_PP_SEQ_ELEM(0,BOOST_PP_SEQ_ELEM(0,BOOST_PP_SEQ_ELEM(n,enums_seq)))\
BOOST_PP_IF(BOOST_PP_GREATER_EQUAL(BOOST_PP_SEQ_SIZE(BOOST_PP_SEQ_ELEM(0,BOOST_PP_SEQ_ELEM(n,enums_seq))),2),\
= BOOST_PP_SEQ_ELEM(1,BOOST_PP_SEQ_ELEM(0,BOOST_PP_SEQ_ELEM(n,enums_seq))),\
BOOST_PP_IF(n,\
= BOOST_PP_SEQ_ELEM(0,BOOST_PP_SEQ_ELEM(0,BOOST_PP_SEQ_ELEM(BOOST_PP_DEC(n),enums_seq)))+1 BOOST_PP_EMPTY,\
BOOST_PP_EMPTY)())
// Generate multiple enumeration#define GENERATE_ENUMS_FROM_SEQ(enums_seq)\
BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(enums_seq),_GENERATE_ENUM_FROM_SEQ,enums_seq)
// Generate single case#define _GENERATE_ENUM_TO_STRING_CASE_2(z,n,enum_seq)\
case BOOST_PP_SEQ_ELEM(0,BOOST_PP_SEQ_ELEM(0,enum_seq)): return \
BOOST_PP_MACRO_IF(BOOST_PP_GREATER_EQUAL(BOOST_PP_SEQ_SIZE(enum_seq),2),\
(1,enum_seq),\
(BOOST_PP_SEQ_ELEM(0,BOOST_PP_SEQ_ELEM(0,enum_seq))),\
BOOST_PP_SEQ_ELEM,\
BOOST_PP_STRINGIZE);
#define _GENERATE_ENUM_TO_STRING_CASE_1(z,n,enums_seq)\
_GENERATE_ENUM_TO_STRING_CASE_2(z,n,BOOST_PP_SEQ_ELEM(n,enums_seq))
// Generate multiple cases#define GENERATE_ENUMS_TO_STRING_CASES(enums_seq)\
BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(enums_seq),_GENERATE_ENUM_TO_STRING_CASE_1,enums_seq)
// Begin of usageenum CommandKind {
GENERATE_ENUMS_FROM_SEQ(ENUM_SEQ_COMMAND_KIND)
};
const char* GetString(CommandKind e) {
switch(e) {
GENERATE_ENUMS_TO_STRING_CASES(ENUM_SEQ_COMMAND_KIND)
}
return 0;
}
После препроцессинга, это будет выглядить примерно так:
BOOST_PP_MACRO_IF нужен из-за проблемы с варнингами, т.к. в BOOST_PP_IF expand'ятся оба аргумента внезависимости от условия, когда нужно чтобы экспандился только один при разных макросах. Возьмёте его здесь
#define ENUM_STR_ENTRY( name ) case name: return #name;
const char* EnumToStr( CommandKind id )
{
StaticAssert( CK_COUNT == 2 );
switch( id ) {
ENUM_STR_ENTRY( CK_INIT );
ENUM_STR_ENTRY( CK_CLOSE );
ENUM_STR_ENTRY( CK_COUNT );
}
assert( false );
return"BadValue";
}
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
enum CommandKind
{
#define COMMAND( NAME ) NAME ,
COMMAND_LIST
#undef COMMAND
};
const char* CommandNames[] =
{
#define COMMAND( NAME ) #NAME ,
COMMAND_LIST
#undef COMMAND
};
Нравилось тем, что легко поддерживать. Отказался из-за того, что члены перечисления невозможно стало документировать Doxygen-ом. Откатился на обычный вариант (Егор его описал тут где-то рядом) с жестким ассертом в default:
It's kind of fun to do the impossible (Walt Disney)