From 834d9408ffa61a6e372cbb875b1652c0469c3f70 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 4 Feb 2022 18:01:07 +0300 Subject: [PATCH] Add psemek_declare_enum macro for common enum utilities --- libs/util/include/psemek/util/enum.hpp | 95 ++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 libs/util/include/psemek/util/enum.hpp diff --git a/libs/util/include/psemek/util/enum.hpp b/libs/util/include/psemek/util/enum.hpp new file mode 100644 index 00000000..7bf272b2 --- /dev/null +++ b/libs/util/include/psemek/util/enum.hpp @@ -0,0 +1,95 @@ +#pragma once + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace psemek::util +{ + + struct unknown_enum_value_exception_base + : std::runtime_error + { + using std::runtime_error::runtime_error; + }; + + template + struct unknown_enum_value_exception + : unknown_enum_value_exception_base + { + unknown_enum_value_exception(Enum value) + : unknown_enum_value_exception_base(to_string("unknown ", type_name(), " value: ", static_cast(value))) + , value_(value) + {} + + Enum value() const noexcept { return value_; } + + private: + Enum value_; + }; + + template + struct enum_iterator + { + using underlying_type = std::underlying_type_t; + + underlying_type value; + + Enum operator *() const { return static_cast(value); } + + enum_iterator operator ++() { ++value; return *this; } + enum_iterator operator ++ (int) + { + enum_iterator copy = *this; + ++(*this); + return copy; + } + + friend bool operator == (enum_iterator const & it1, enum_iterator const & it2) = default; + friend bool operator != (enum_iterator const & it1, enum_iterator const & it2) = default; + }; + + template + struct enum_range + { + enum_iterator first, last; + + auto begin() const { return first; } + auto end() const { return last; } + }; + +} + +#define psemek_declare_enum_detail_enumerator(r, data, value) value, + +#define psemek_declare_enum_detail_to_string(r, data, value) case data :: value : return BOOST_PP_STRINGIZE(value); + +#define psemek_declare_enum_detail_from_string(r, data, value) { BOOST_PP_STRINGIZE(value), data::value }, + +#define psemek_declare_enum(name, type, values) \ + enum class name : type { \ + BOOST_PP_SEQ_FOR_EACH(psemek_declare_enum_detail_enumerator, _, values) \ + }; \ + inline ::std::string_view to_string(name value) { \ + switch (value) { \ + BOOST_PP_SEQ_FOR_EACH(psemek_declare_enum_detail_to_string, name, values) \ + default: throw ::psemek::util::unknown_enum_value_exception(value); \ + } \ + } \ + inline std::optional BOOST_PP_CAT(name, _from_string)(::std::string_view str) { \ + static const ::std::unordered_map<::std::string_view, name> map = { \ + BOOST_PP_SEQ_FOR_EACH(psemek_declare_enum_detail_from_string, name, values) \ + }; \ + auto it = map.find(str); if (it == map.end()) return std::nullopt; return it->second; \ + } \ + inline ::psemek::util::enum_range BOOST_PP_CAT(name, _values) () { return {0, BOOST_PP_SEQ_SIZE(values)}; } + +