#ifndef GI_ENUMFLAG_HPP #define GI_ENUMFLAG_HPP #include "base.hpp" #include "exception.hpp" #include "value.hpp" #include namespace gi { namespace detail { struct EnumValueTraits { typedef GEnumClass class_type; typedef GEnumValue value_type; static class_type *get_class(GType gtype) { auto c = g_type_class_peek(gtype); return (class_type *)(c ? G_ENUM_CLASS(c) : c); } static value_type *get_value(class_type *klass, int v) { return g_enum_get_value(klass, v); } static value_type *get_by_name(class_type *klass, const char *name) { return g_enum_get_value_by_name(klass, name); } static value_type *get_by_nick(class_type *klass, const char *name) { return g_enum_get_value_by_nick(klass, name); } }; struct FlagsValueTraits { typedef GFlagsClass class_type; typedef GFlagsValue value_type; static class_type *get_class(GType gtype) { auto c = g_type_class_peek(gtype); return (class_type *)(c ? G_FLAGS_CLASS(c) : c); } static value_type *get_value(class_type *klass, int v) { return g_flags_get_first_value(klass, v); } static value_type *get_by_name(class_type *klass, const char *name) { return g_flags_get_value_by_name(klass, name); } static value_type *get_by_nick(class_type *klass, const char *name) { return g_flags_get_value_by_nick(klass, name); } }; template class EnumValue { typedef EnumValue self; typedef typename Traits::value_type value_type; typedef typename Traits::class_type class_type; value_type *value_; static class_type *get_class() { auto c = Traits::get_class(get_type_()); if (!c) { detail::try_throw(std::invalid_argument("unknown class")); } else { return c; } } EnumValue(gint v) : EnumValue(static_cast(v)) {} EnumValue(value_type *_value) : value_(_value) {} public: typedef EnumType enum_type; static GType get_type_() { return traits::gtype::get_type(); } EnumValue(EnumType v) : value_(Traits::get_value(get_class(), (int)v)) { if (!value_) detail::try_throw(std::invalid_argument( "invalid value " + std::to_string((unsigned)v))); } static self get_by_name(const gi::cstring_v name) { auto v = Traits::get_by_name(get_class(), name.c_str()); if (!v) detail::try_throw(std::invalid_argument("unknown name")); else return self(v); } static self get_by_nick(const gi::cstring_v nick) { auto v = Traits::get_by_nick(get_class(), nick.c_str()); if (!v) detail::try_throw(std::invalid_argument("unknown nick")); else return self(v); } operator EnumType() const noexcept { return static_cast(value_->value); } gi::cstring_v value_name() const noexcept { return value_->value_name; } gi::cstring_v value_nick() const noexcept { return value_->value_nick; } }; } // namespace detail // enumeration nick/name helpers template using EnumValue = detail::EnumValue; template::value && traits::gtype::value>::type * = nullptr> inline EnumValue value_info(EnumType v) { return EnumValue(v); } // bitfield nick/name helpers template using FlagsValue = detail::EnumValue; template::value && traits::gtype::value>::type * = nullptr> inline FlagsValue value_info(EnumType v) { return FlagsValue(v); } // bitfield operation helpers template::value>::type * = nullptr> inline FlagType operator|(FlagType lhs, FlagType rhs) { return static_cast( static_cast(lhs) | static_cast(rhs)); } template::value>::type * = nullptr> inline FlagType operator&(FlagType lhs, FlagType rhs) { return static_cast( static_cast(lhs) & static_cast(rhs)); } template::value>::type * = nullptr> inline FlagType operator^(FlagType lhs, FlagType rhs) { return static_cast( static_cast(lhs) ^ static_cast(rhs)); } template::value>::type * = nullptr> inline FlagType operator~(FlagType flags) { return static_cast(~static_cast(flags)); } template::value>::type * = nullptr> inline FlagType & operator|=(FlagType &lhs, FlagType rhs) { return (lhs = static_cast( static_cast(lhs) | static_cast(rhs))); } template::value>::type * = nullptr> inline FlagType & operator&=(FlagType &lhs, FlagType rhs) { return (lhs = static_cast( static_cast(lhs) & static_cast(rhs))); } template::value>::type * = nullptr> inline FlagType & operator^=(FlagType &lhs, FlagType rhs) { return (lhs = static_cast( static_cast(lhs) ^ static_cast(rhs))); } } // namespace gi // sadly, the above templates are not picked up by ADL // and might therefore only end up used in case of a `using namespace gi` // so also explicitly add operators in generated namespace // (by means of macro to simplify generation) #define GI_FLAG_OPERATORS(FlagType) \ inline FlagType operator|(FlagType lhs, FlagType rhs) \ { \ return static_cast( \ static_cast(lhs) | static_cast(rhs)); \ } \ \ inline FlagType operator&(FlagType lhs, FlagType rhs) \ { \ return static_cast( \ static_cast(lhs) & static_cast(rhs)); \ } \ \ inline FlagType operator^(FlagType lhs, FlagType rhs) \ { \ return static_cast( \ static_cast(lhs) ^ static_cast(rhs)); \ } \ \ inline FlagType operator~(FlagType flags) \ { \ return static_cast(~static_cast(flags)); \ } \ \ inline FlagType &operator|=(FlagType &lhs, FlagType rhs) \ { \ return (lhs = static_cast( \ static_cast(lhs) | static_cast(rhs))); \ } \ \ inline FlagType &operator&=(FlagType &lhs, FlagType rhs) \ { \ return (lhs = static_cast( \ static_cast(lhs) & static_cast(rhs))); \ } \ \ inline FlagType &operator^=(FlagType &lhs, FlagType rhs) \ { \ return (lhs = static_cast( \ static_cast(lhs) ^ static_cast(rhs))); \ } #define GI_ENUM_NUMERIC(EnumType) \ inline std::underlying_type::type operator+(EnumType e) \ { \ return static_cast::type>(e); \ } /* NOTE: * * enumeration/bitfield TypeX is currently represented by an enum class TypeX * along with a namespace TypeXNS_ that holds the member functions. * * This could be merged into single class TypeX * (possibly using templated helpers); * * class TypeX * { * enum Enum { * VALUE_A, * VALUE_B * } * Enum value; * * (or C++17): * inline static const TypeX VALUE_A; * (note; constexpr not possible with incomplete type) * * constructor (Enum) * operator Enum() * ... etc ... * std::string value_name(); [opt] * std::string value_nick(); [opt] * } * * Disadvantage is that TypeX::VALUE_A would not have type TypeX, * although easily converted from/to TypeX, and so it would mostly work. * However, the operators above would have to be (even more) complicated and * accept various combinations of TypeX and TypeX::Enum (in case of a bitfield). * * All in all, the type mismatch (and moderately rare member function * occurrence) does not warrant going this way at this time. Also avoid C++17 * for now, so as it stands ... */ #endif // GI_ENUMFLAG_HPP