enumflag.hpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. #ifndef GI_ENUMFLAG_HPP
  2. #define GI_ENUMFLAG_HPP
  3. #include "base.hpp"
  4. #include "exception.hpp"
  5. #include "value.hpp"
  6. #include <type_traits>
  7. namespace gi
  8. {
  9. namespace detail
  10. {
  11. struct EnumValueTraits
  12. {
  13. typedef GEnumClass class_type;
  14. typedef GEnumValue value_type;
  15. static class_type *get_class(GType gtype)
  16. {
  17. auto c = g_type_class_peek(gtype);
  18. return (class_type *)(c ? G_ENUM_CLASS(c) : c);
  19. }
  20. static value_type *get_value(class_type *klass, int v)
  21. {
  22. return g_enum_get_value(klass, v);
  23. }
  24. static value_type *get_by_name(class_type *klass, const char *name)
  25. {
  26. return g_enum_get_value_by_name(klass, name);
  27. }
  28. static value_type *get_by_nick(class_type *klass, const char *name)
  29. {
  30. return g_enum_get_value_by_nick(klass, name);
  31. }
  32. };
  33. struct FlagsValueTraits
  34. {
  35. typedef GFlagsClass class_type;
  36. typedef GFlagsValue value_type;
  37. static class_type *get_class(GType gtype)
  38. {
  39. auto c = g_type_class_peek(gtype);
  40. return (class_type *)(c ? G_FLAGS_CLASS(c) : c);
  41. }
  42. static value_type *get_value(class_type *klass, int v)
  43. {
  44. return g_flags_get_first_value(klass, v);
  45. }
  46. static value_type *get_by_name(class_type *klass, const char *name)
  47. {
  48. return g_flags_get_value_by_name(klass, name);
  49. }
  50. static value_type *get_by_nick(class_type *klass, const char *name)
  51. {
  52. return g_flags_get_value_by_nick(klass, name);
  53. }
  54. };
  55. template<typename EnumType, typename Traits>
  56. class EnumValue
  57. {
  58. typedef EnumValue self;
  59. typedef typename Traits::value_type value_type;
  60. typedef typename Traits::class_type class_type;
  61. value_type *value_;
  62. static class_type *get_class()
  63. {
  64. auto c = Traits::get_class(get_type_());
  65. if (!c) {
  66. detail::try_throw(std::invalid_argument("unknown class"));
  67. } else {
  68. return c;
  69. }
  70. }
  71. EnumValue(gint v) : EnumValue(static_cast<EnumType>(v)) {}
  72. EnumValue(value_type *_value) : value_(_value) {}
  73. public:
  74. typedef EnumType enum_type;
  75. static GType get_type_() { return traits::gtype<EnumType>::get_type(); }
  76. EnumValue(EnumType v) : value_(Traits::get_value(get_class(), (int)v))
  77. {
  78. if (!value_)
  79. detail::try_throw(std::invalid_argument(
  80. "invalid value " + std::to_string((unsigned)v)));
  81. }
  82. static self get_by_name(const gi::cstring_v name)
  83. {
  84. auto v = Traits::get_by_name(get_class(), name.c_str());
  85. if (!v)
  86. detail::try_throw(std::invalid_argument("unknown name"));
  87. else
  88. return self(v);
  89. }
  90. static self get_by_nick(const gi::cstring_v nick)
  91. {
  92. auto v = Traits::get_by_nick(get_class(), nick.c_str());
  93. if (!v)
  94. detail::try_throw(std::invalid_argument("unknown nick"));
  95. else
  96. return self(v);
  97. }
  98. operator EnumType() const noexcept
  99. {
  100. return static_cast<EnumType>(value_->value);
  101. }
  102. gi::cstring_v value_name() const noexcept { return value_->value_name; }
  103. gi::cstring_v value_nick() const noexcept { return value_->value_nick; }
  104. };
  105. } // namespace detail
  106. // enumeration nick/name helpers
  107. template<typename EnumType>
  108. using EnumValue = detail::EnumValue<EnumType, detail::EnumValueTraits>;
  109. template<typename EnumType,
  110. typename std::enable_if<traits::is_enumeration<EnumType>::value &&
  111. traits::gtype<EnumType>::value>::type * = nullptr>
  112. inline EnumValue<EnumType>
  113. value_info(EnumType v)
  114. {
  115. return EnumValue<EnumType>(v);
  116. }
  117. // bitfield nick/name helpers
  118. template<typename EnumType>
  119. using FlagsValue = detail::EnumValue<EnumType, detail::FlagsValueTraits>;
  120. template<typename EnumType,
  121. typename std::enable_if<traits::is_bitfield<EnumType>::value &&
  122. traits::gtype<EnumType>::value>::type * = nullptr>
  123. inline FlagsValue<EnumType>
  124. value_info(EnumType v)
  125. {
  126. return FlagsValue<EnumType>(v);
  127. }
  128. // bitfield operation helpers
  129. template<typename FlagType,
  130. typename std::enable_if<traits::is_bitfield<FlagType>::value>::type * =
  131. nullptr>
  132. inline FlagType
  133. operator|(FlagType lhs, FlagType rhs)
  134. {
  135. return static_cast<FlagType>(
  136. static_cast<unsigned>(lhs) | static_cast<unsigned>(rhs));
  137. }
  138. template<typename FlagType,
  139. typename std::enable_if<traits::is_bitfield<FlagType>::value>::type * =
  140. nullptr>
  141. inline FlagType
  142. operator&(FlagType lhs, FlagType rhs)
  143. {
  144. return static_cast<FlagType>(
  145. static_cast<unsigned>(lhs) & static_cast<unsigned>(rhs));
  146. }
  147. template<typename FlagType,
  148. typename std::enable_if<traits::is_bitfield<FlagType>::value>::type * =
  149. nullptr>
  150. inline FlagType
  151. operator^(FlagType lhs, FlagType rhs)
  152. {
  153. return static_cast<FlagType>(
  154. static_cast<unsigned>(lhs) ^ static_cast<unsigned>(rhs));
  155. }
  156. template<typename FlagType,
  157. typename std::enable_if<traits::is_bitfield<FlagType>::value>::type * =
  158. nullptr>
  159. inline FlagType
  160. operator~(FlagType flags)
  161. {
  162. return static_cast<FlagType>(~static_cast<unsigned>(flags));
  163. }
  164. template<typename FlagType,
  165. typename std::enable_if<traits::is_bitfield<FlagType>::value>::type * =
  166. nullptr>
  167. inline FlagType &
  168. operator|=(FlagType &lhs, FlagType rhs)
  169. {
  170. return (lhs = static_cast<FlagType>(
  171. static_cast<unsigned>(lhs) | static_cast<unsigned>(rhs)));
  172. }
  173. template<typename FlagType,
  174. typename std::enable_if<traits::is_bitfield<FlagType>::value>::type * =
  175. nullptr>
  176. inline FlagType &
  177. operator&=(FlagType &lhs, FlagType rhs)
  178. {
  179. return (lhs = static_cast<FlagType>(
  180. static_cast<unsigned>(lhs) & static_cast<unsigned>(rhs)));
  181. }
  182. template<typename FlagType,
  183. typename std::enable_if<traits::is_bitfield<FlagType>::value>::type * =
  184. nullptr>
  185. inline FlagType &
  186. operator^=(FlagType &lhs, FlagType rhs)
  187. {
  188. return (lhs = static_cast<FlagType>(
  189. static_cast<unsigned>(lhs) ^ static_cast<unsigned>(rhs)));
  190. }
  191. } // namespace gi
  192. // sadly, the above templates are not picked up by ADL
  193. // and might therefore only end up used in case of a `using namespace gi`
  194. // so also explicitly add operators in generated namespace
  195. // (by means of macro to simplify generation)
  196. #define GI_FLAG_OPERATORS(FlagType) \
  197. inline FlagType operator|(FlagType lhs, FlagType rhs) \
  198. { \
  199. return static_cast<FlagType>( \
  200. static_cast<unsigned>(lhs) | static_cast<unsigned>(rhs)); \
  201. } \
  202. \
  203. inline FlagType operator&(FlagType lhs, FlagType rhs) \
  204. { \
  205. return static_cast<FlagType>( \
  206. static_cast<unsigned>(lhs) & static_cast<unsigned>(rhs)); \
  207. } \
  208. \
  209. inline FlagType operator^(FlagType lhs, FlagType rhs) \
  210. { \
  211. return static_cast<FlagType>( \
  212. static_cast<unsigned>(lhs) ^ static_cast<unsigned>(rhs)); \
  213. } \
  214. \
  215. inline FlagType operator~(FlagType flags) \
  216. { \
  217. return static_cast<FlagType>(~static_cast<unsigned>(flags)); \
  218. } \
  219. \
  220. inline FlagType &operator|=(FlagType &lhs, FlagType rhs) \
  221. { \
  222. return (lhs = static_cast<FlagType>( \
  223. static_cast<unsigned>(lhs) | static_cast<unsigned>(rhs))); \
  224. } \
  225. \
  226. inline FlagType &operator&=(FlagType &lhs, FlagType rhs) \
  227. { \
  228. return (lhs = static_cast<FlagType>( \
  229. static_cast<unsigned>(lhs) & static_cast<unsigned>(rhs))); \
  230. } \
  231. \
  232. inline FlagType &operator^=(FlagType &lhs, FlagType rhs) \
  233. { \
  234. return (lhs = static_cast<FlagType>( \
  235. static_cast<unsigned>(lhs) ^ static_cast<unsigned>(rhs))); \
  236. }
  237. #define GI_ENUM_NUMERIC(EnumType) \
  238. inline std::underlying_type<EnumType>::type operator+(EnumType e) \
  239. { \
  240. return static_cast<std::underlying_type<EnumType>::type>(e); \
  241. }
  242. /* NOTE:
  243. *
  244. * enumeration/bitfield TypeX is currently represented by an enum class TypeX
  245. * along with a namespace TypeXNS_ that holds the member functions.
  246. *
  247. * This could be merged into single class TypeX
  248. * (possibly using templated helpers);
  249. *
  250. * class TypeX
  251. * {
  252. * enum Enum {
  253. * VALUE_A,
  254. * VALUE_B
  255. * }
  256. * Enum value;
  257. *
  258. * (or C++17):
  259. * inline static const TypeX VALUE_A;
  260. * (note; constexpr not possible with incomplete type)
  261. *
  262. * constructor (Enum)
  263. * operator Enum()
  264. * ... etc ...
  265. * std::string value_name(); [opt]
  266. * std::string value_nick(); [opt]
  267. * }
  268. *
  269. * Disadvantage is that TypeX::VALUE_A would not have type TypeX,
  270. * although easily converted from/to TypeX, and so it would mostly work.
  271. * However, the operators above would have to be (even more) complicated and
  272. * accept various combinations of TypeX and TypeX::Enum (in case of a bitfield).
  273. *
  274. * All in all, the type mismatch (and moderately rare member function
  275. * occurrence) does not warrant going this way at this time. Also avoid C++17
  276. * for now, so as it stands ...
  277. */
  278. #endif // GI_ENUMFLAG_HPP