#pragma once #include #include #include #include #include #include #include template class EnumConverter { public: EnumConverter() : initialized(false) {} virtual ~EnumConverter() = default; virtual boost::optional tryToString(TEnum value) { initialize(); auto it = valueToNameMap.find(value); return it != valueToNameMap.end() ? it->second : boost::optional(); } std::string toString(TEnum value) { initialize(); auto result = tryToString(value); if (!result) { auto numericValue = static_cast::type>(value); throw std::invalid_argument(fmt::format("{} is not a valid {} value.", numericValue, typeName)); } return result.value(); } virtual boost::optional tryParse(const std::string& s) { initialize(); auto it = lowerCaseNameToValueMap.find(boost::algorithm::to_lower_copy(s)); return it != lowerCaseNameToValueMap.end() ? it->second : boost::optional(); } TEnum parse(const std::string& s) { initialize(); auto result = tryParse(s); if (!result) { throw std::invalid_argument(fmt::format("{} is not a valid {} value.", s, typeName)); } return result.value(); } std::ostream& write(std::ostream& stream, TEnum value) { return stream << toString(value); } std::istream& read(std::istream& stream, TEnum& value) { std::string name; stream >> name; value = parse(name); return stream; } const std::vector& getValues() { initialize(); return values; } protected: using member_data = std::vector>; virtual std::string getTypeName() = 0; virtual member_data getMemberData() = 0; private: void initialize() { if (initialized) return; typeName = getTypeName(); for (const auto& pair : getMemberData()) { TEnum value = pair.first; std::string name = pair.second; lowerCaseNameToValueMap[boost::algorithm::to_lower_copy(name)] = value; valueToNameMap[value] = name; values.push_back(value); } initialized = true; } bool initialized; std::string typeName; std::map lowerCaseNameToValueMap; std::map valueToNameMap; std::vector values; };