rhubarb-lip-sync/src/EnumConverter.h

100 lines
2.3 KiB
C
Raw Normal View History

#pragma once
#include <initializer_list>
#include <utility>
#include <map>
#include <vector>
#include <boost/algorithm/string.hpp>
#include <boost/optional.hpp>
#include <format.h>
template<typename TEnum>
class EnumConverter {
public:
EnumConverter() :
initialized(false)
{}
virtual ~EnumConverter() = default;
virtual boost::optional<std::string> tryToString(TEnum value) {
initialize();
auto it = valueToNameMap.find(value);
return it != valueToNameMap.end()
? it->second
: boost::optional<std::string>();
}
std::string toString(TEnum value) {
initialize();
auto result = tryToString(value);
if (!result) {
auto numericValue = static_cast<typename std::underlying_type<TEnum>::type>(value);
throw std::invalid_argument(fmt::format("{} is not a valid {} value.", numericValue, typeName));
}
return result.value();
}
virtual boost::optional<TEnum> 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>();
}
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<TEnum>& getValues() {
initialize();
return values;
}
protected:
using member_data = std::vector<std::pair<TEnum, std::string>>;
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<std::string, TEnum> lowerCaseNameToValueMap;
std::map<TEnum, std::string> valueToNameMap;
std::vector<TEnum> values;
};