Add psemek_declare_enum macro for common enum utilities
This commit is contained in:
parent
a01bd019f8
commit
834d9408ff
1 changed files with 95 additions and 0 deletions
95
libs/util/include/psemek/util/enum.hpp
Normal file
95
libs/util/include/psemek/util/enum.hpp
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
#pragma once
|
||||
|
||||
#include <boost/preprocessor/seq/for_each.hpp>
|
||||
#include <boost/preprocessor/stringize.hpp>
|
||||
|
||||
#include <psemek/util/to_string.hpp>
|
||||
#include <psemek/util/type_name.hpp>
|
||||
|
||||
#include <string_view>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
struct unknown_enum_value_exception_base
|
||||
: std::runtime_error
|
||||
{
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
template <typename Enum>
|
||||
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<Enum>(), " value: ", static_cast<std::int64_t>(value)))
|
||||
, value_(value)
|
||||
{}
|
||||
|
||||
Enum value() const noexcept { return value_; }
|
||||
|
||||
private:
|
||||
Enum value_;
|
||||
};
|
||||
|
||||
template <typename Enum>
|
||||
struct enum_iterator
|
||||
{
|
||||
using underlying_type = std::underlying_type_t<Enum>;
|
||||
|
||||
underlying_type value;
|
||||
|
||||
Enum operator *() const { return static_cast<Enum>(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 <typename Enum>
|
||||
struct enum_range
|
||||
{
|
||||
enum_iterator<Enum> 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<name>(value); \
|
||||
} \
|
||||
} \
|
||||
inline std::optional<name> 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<name> BOOST_PP_CAT(name, _values) () { return {0, BOOST_PP_SEQ_SIZE(values)}; }
|
||||
|
||||
|
||||
Loading…
Add table
Reference in a new issue