[Из песочницы] Статические отображения
Часто приходится сталкиваться в такими моментами, как отображение перечисления в строку и обратно, либо в фиксированное значения для дальнейшей сериализации. Здесь приводиться способ однозначного статического отображения значения типа A в значение типа B и обратно. Я попытался наделить метод небольшой гибкостью, позволяющей легко расширять варианты отображений.В кратце суть метода состоит в том, что имеется статический контейнер — карта отображений —, а так же ряд вспомогательных функций, расширяемых под нужды проекта и избавляющих от прямого взаимодействия с контейнером.В итоге преобразование из исходного типа в строку и обратно будет выглядеть так:
// Исходный тип enum class Fruit{ Unknown, Apple, Banana, Orange, };
// Преобразование в строку string fruitStr = toString (Fruit: Orange);
// Обратное преобразование из строки в исходный тип
Fruit fruit = stringTo
// Вспомогательный хранитель типа для разрешения перегрузки
template
// Сигнатура функции получения доступа к контейнеру отображения
ContainerType const& viewMapOf (TypeHolder
template
// Структура отображения struct View: VariantsT { View (SourceT id=SourceT (), VariantsT vnt=VariantsT ()): VariantsT (vnt), origin (id) { ;; }
bool operator<(View const& b) const { return origin < b.origin ; }
SourceT origin; //< Исходное значение };
using Views = std: set
ViewMap () { ;; }
ViewMap (std: initializer_list
// Получение исходного типа static SourceT extractOrigin (View const& view) { return view.origin; }
Views views; // Карта отображений View invalidView; // Отображение ошибки } ; Для отображения в строку необходимо расширить поля структуры VievMap: View. Дополнительные поля я называю вариантами. Вот как выглядит готовый шаблон контейнера:
struct StringVariant { StringVariant (std: string const& s = »): str (s) { ;; }
std: string str; };
// Карта отображений в строку
template< typename SourceT >
struct StringViewMap: ViewMap
StringViewMap () { ;; }
StringViewMap (std: initializer_list
// Извлечение строки из отображения static std: string const& extractString (typename Base: View const& view) { return view.str; } } ; Как видно, StringViewMap наследует весь базовый функционал ViewMap, расширяя его вспомогательной функцией extractString.Теперь реализовать функционал toString, stringTo очень просто:
template
MapRef map = viewMapOf (TypeHolder
auto const& views = map.views; auto pos = views.find (typename PureMap: View (id)) ;
return PureMap: extractString ((pos!= views.end ()) ? *pos: map.invalidView) ; }
template
MapRef map = viewMapOf (TypeHolder
auto pos = std: find_if ( views.begin (), views.end (), [&](typename PureMap: View const& val) { return PureMap: extractString (val) == str; } ) ; return PureMap: extractOrigin ((pos!= views.end ()) ? *pos: map.invalidView) ; } Весь секрет toString и stringTo в использовании интерфейса контейнера —, а именно его фукнций extractOrigin и extractString. Таким образом stringTo, toString будет работать только с теми отображениями, что предоставляют интерфейс extractString.
ViewMapTraits необходим ввиду того, что сигнатура перегруженной функции viewMapOf может отличаться для разных перегрузок, а именно возвращаемое значение, может быть как ссылкой так и объектом. Вот каков он внутри:
template
StringViewMap
enum class Mix { Unknown, RedApple, GreenApple, GreenBanana, BigOrange, SmallOrange, };
// Варианты отображения для Mix struct MixVariant { MixVariant (Fruit f = Fruit: Unknown, std: string const& s = »): fruit (f), str (s) { ;; }
Fruit fruit; // Классификация фрукта std: string str; // Строковое представление };
// Карта отображений
struct MixViewMap: ViewMap
MixViewMap () { ;; }
MixViewMap (std: initializer_list
// Интерфейс для toString, stringTo static std: string const& extractString (typename Base: View const& view) { return view.str; }
// Интерфейс для toFruit static std: string const& extractFruit (typename Base: View const& view) { return view.fruit; } } ;
// Заполняем карту
MixViewMap const& viewMapOf (TypeHolder
// Вспомогательная функция классификации
template
MapRef map = viewMapOf (TypeHolder
auto const& views = map.views; auto pos = views.find (typename PureMap: View (id)) ;
return PureMap: extractFruit ((pos!= views.end ()) ? *pos: map.invalidView) ; } Здесь добавили дополнительную функцию — классификатор toFruit. Смысл ее тот же, что и у toString, изменилось немного содержание. Теперь продемонстрирую работу преобразований:
string redAppleStr = «red_apple»;
Mix mix = stringTo
Fruit mixFruit = toFruit (mix); // mixFruit == Fruit: Apple
string mixFruitStr = toString (mixFruit); // mixFruitStr == «apple»
Применяю данную технику в своих проектах — очень удобно. Наверняка есть идеи по улучшению — здесь я изложил основной подход.