#ifndef RFL_INTERNAL_ENUMS_STRINGCONVERTER_HPP_ #define RFL_INTERNAL_ENUMS_STRINGCONVERTER_HPP_ #include #include #include #include #include #include "../../Result.hpp" #include "../../internal/strings/join.hpp" #include "../../internal/strings/split.hpp" #include "../../type_name_t.hpp" #include "get_enum_names.hpp" #include "is_flag_enum.hpp" namespace rfl { namespace internal { namespace enums { template class StringConverter { public: static constexpr bool is_flag_enum_ = is_flag_enum; static constexpr auto names_ = get_enum_names(); using NamesLiteral = typename decltype(names_)::Literal; public: /// Transform an enum to a matching string. static std::string enum_to_string(const EnumType _enum) { if constexpr (is_flag_enum_) { return flag_enum_to_string(_enum); } else { return enum_to_single_string(_enum); } } /// Transforms a string to the matching enum. static Result string_to_enum(const std::string& _str) { static_assert( names_.size != 0, "No enum could be identified. Please choose enum values " "between 0 to 127 or for flag enums choose 1,2,4,8,16,..."); if constexpr (is_flag_enum_) { return string_to_flag_enum(_str); } else { return single_string_to_enum(_str); } } private: /// Iterates through the enum bit by bit and matches it against the /// flags. static std::string flag_enum_to_string(const EnumType _e) { using T = std::underlying_type_t; auto val = static_cast(_e); int i = 0; std::vector flags; while (val != 0) { const auto bit = val & static_cast(1); if (bit == 1) { auto str = enum_to_single_string( static_cast(static_cast(1) << i)); flags.emplace_back(std::move(str)); } ++i; val >>= 1; } return strings::join("|", flags); } /// This assumes that _enum can be exactly matched to one of the names /// and does not have to be combined using |. static std::string enum_to_single_string(const EnumType _enum) { const auto to_str = [](const auto _l) { return _l.str(); }; for (size_t i = 0; i < names_.size; ++i) { if (names_.enums_[i] == _enum) { return NamesLiteral::from_value( static_cast(i)) .transform(to_str) .value(); } } return std::to_string( static_cast>(_enum)); } /// Finds the enum matching the literal. static EnumType literal_to_enum(const NamesLiteral _lit) noexcept { return names_.enums_[_lit.value()]; } /// This assumes that _enum can be exactly matched to one of the names /// and does not have to be combined using |. static Result single_string_to_enum(const std::string& _str) { const auto r = NamesLiteral::from_string(_str).transform(literal_to_enum); if (r) { return r; } else { try { return static_cast(std::stoi(_str)); } catch (std::exception& exp) { return Error(exp.what()); } } } /// Only relevant if this is a flag enum - combines the different /// matches using |. static Result string_to_flag_enum( const std::string& _str) noexcept { using T = std::underlying_type_t; const auto split = strings::split(_str, "|"); auto res = static_cast(0); for (const auto& s : split) { const auto r = single_string_to_enum(s); if (r) { res |= static_cast(*r); } else { return r; } } return static_cast(res); } }; } // namespace enums } // namespace internal } // namespace rfl #endif