Add psemek_declare_enum macro for common enum utilities

This commit is contained in:
Nikita Lisitsa 2022-02-04 18:01:07 +03:00
parent a01bd019f8
commit 834d9408ff

View 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)}; }