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