#cpp
Есть список UnicodeString констант и соответствующий ему список целочисленных констант: const UnicodeString ERR = L"Error"; const UnicodeString READY = L"Ready"; ... const int S_ERR = 0; const int S_READY = 1; ... Не знаю, какой контейнер для хранения и удобной работы с этими данными использовать. Если использовать их отдельно, то получается крайне неудобно с ними работать, когда надо получать строку в зависимости от состояния и подобные действия: switch (Status) { case S_ERR: return ERR; ... } Какой контейнер можно использовать для этого? Очень важен ответ, часто использую такого рода логику.
Ответы
Ответ 1
Раз у Вас есть ассоциативное отношение, то сам собой напрашивается ассоциативный контейнер, а именно: std::unordered_map: ... const int S_ERR = 0; const int S_READY = 1; ... std::unordered_mapstrings; strings.emplace(S_ERR, L"Error"); strings.emplace(S_READY, L"Ready"); ... return strings[Status]; Это позволяет нам не задумывать о том, в каком порядке, что хранится и не пользоваться switch, который только раздувает код. Ответ 2
Если константы идут строго подряд - std::vector (std::array). Если с дырами - std::map Пример с вектором (ideone) #include#include using namespace std; static constexpr array strs ={L"azaza",L"ololo"}; int main() { for(const auto i:strs) { wcout << i << endl; } return 0; } А для локализации существует PoEdit и связанный с ним фреймворк. Ответ 3
Применение контейнеров в данном простом случае избыточно. Для решения описанной вами задачи, я определяю перечисление с числовыми константами и пишу функцию которая выполняет сопоставление чисел и строк. Теперь необходимо следить за этим сопоставлением. Чтобы о нем не забыть, я добавляю assert в функции сопоставления плюс вывожу какую-либо заметую строку. Assert срабатывает в отладочной сборке, а "заметная строка" в выпускной сборке. При таком подходе, за мою практику ниразу в выпускной сборке не появлялись несопоставленные константы, 100% забытых сопоставлений отлавливались на этапе отладки с помощью assert. Дополнительно, обычно если значения приходят откуда-то извне из файла или из сети, я пишу функцию проверки значений на соответсвие константам. Вот примерно такой код получается: enum StatusCodes { STATUS_SUCCESS , STATUS_WARNING , STATUS_ERROR , STATUS_CRASH , STATUS_UNKNOWN }; const wchar_t * StatusCodeLabel(int code) { switch(code) { case STATUS_SUCCESS: return L"OK" ; case STATUS_WARNING: return L"Warning"; case STATUS_ERROR : return L"Error" ; case STATUS_CRASH : return L"Crash" ; } assert(false); return L"STATUS CODE WRONG!"; } StatusCodes ValidateStatusCode(int code) { switch(code) { case STATUS_SUCCESS: case STATUS_WARNING: case STATUS_ERROR : case STATUS_CRASH : return code; } assert(false); return STATUS_UNKNOWN; } Такой подход наиболее дешев в сопровождении и эффективен в коде, даже когда констант очень много. Почему-то именно про сопровождение обычно забывают. Поставте себя на место программиста который в первый раз читает код с "контейнерным" сопоставлением. Он видит контейнер, константы, строки какие-то и первые мысли у него будут что это часть какой-то прикладной логики, а не простое сопоставление строк константам. Вот так по чуть-чуть накапливается ненужная сложность.Ответ 4
Это смотря для чего нужно. Иногда из таких констант нужно генерировать enum'ы или функции. Тогда поможет boost.preprocessor. Пример из недавнего проекта: #define SB_PROP_TYPES \ /* type name, type, is pointer, is native, array length */ \ /* note: all non-pointer types should be listed */ \ /* in `sb_config_entry`. */ \ /* here, `$` is a valid pointer to `sb_config_entry`, */ \ /* `$$` is a `v_ptr` casted into a proper type. */ \ ((SB_STRING, char, 1, 1, (strlen($$) + 1) )) \ ((SB_LITERAL, char, 1, 1, (strlen($$) + 1) )) \ ((SB_INT, int, 0, 1, )) \ ((SB_BOOL, bool, 0, 1, )) \ ((SB_CHAR_PAIR_ARR, sb_prop_char_pair_array, 1, 0, (1) )) \ ((__SB_UNKNOWN, int, 0, 1, )) // ... enum sb_prop_types { #define __SB_OP(r, data, elem) (BOOST_PP_TUPLE_ELEM(5, 0, elem)) BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH(__SB_OP, , SB_PROP_TYPES)) #undef __SB_OP }; Такая штука развернется в enum sb_prop_types { SB_STRING, SB_LITERAL, SB_INT, SB_BOOL, SB_CHAR_PAIR_ARR, __SB_UNKNOWN }; Далее, есть такой пример — выполняет роль вашего switch (работа со строками, так что используется много ифов, но вообще можно и switch): static inline enum sb_prop_types prop_to_type(const char *__name) { #define __SB_OP(r, data, elem) \ if (strcasecmp(BOOST_PP_TUPLE_ELEM(3, 0, elem), __name) == 0) \ return BOOST_PP_TUPLE_ELEM(3, 2, elem); BOOST_PP_SEQ_FOR_EACH(__SB_OP, _, SB_PROPS) #undef __SB_OP return __SB_UNKNOWN; } Соответственно, развернется в большое количество ифов.
Комментариев нет:
Отправить комментарий