opaque_typedef.hpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. //
  2. // opaque_typedef.hpp
  3. // opaque-type
  4. //
  5. // Inspired by https://foonathan.github.io/blog/2016/10/19/strong-typedefs.html and others
  6. //
  7. // Created by Sam Jaffe on 8/16/16.
  8. //
  9. #pragma once
  10. #include <type_traits>
  11. template <typename, typename> class opaque_typedef;
  12. template <typename Tag, typename T>
  13. T underlying_type_impl(opaque_typedef<Tag, T>);
  14. template <typename T>
  15. using underlying_type = decltype(underlying_type_impl(std::declval<T>()));
  16. #define OPAQUE_TYPE_ASSIGN_OPERATOR(name, op) \
  17. template <typename TDef> \
  18. struct name { \
  19. friend TDef & operator op##=(TDef & lhs, TDef const & rhs) { \
  20. using type = underlying_type<TDef>; \
  21. static_cast<type &>(lhs) op##= static_cast<type const &>(rhs); \
  22. return lhs; \
  23. } \
  24. \
  25. friend TDef operator op(TDef const & lhs, TDef const & rhs) { \
  26. using type = underlying_type<TDef>; \
  27. return TDef(static_cast<type const&>(lhs) op static_cast<type const&>(rhs)); \
  28. } \
  29. }
  30. #define OPAQUE_TYPE_BINARY_OPERATOR_T(name, op, Out) \
  31. template <typename TDef> \
  32. struct name { \
  33. friend Out operator op(TDef const & lhs, TDef const & rhs) { \
  34. using type = underlying_type<TDef>; \
  35. return Out(static_cast<type const&>(lhs) op static_cast<type const&>(rhs)); \
  36. } \
  37. }
  38. #define OPAQUE_TYPE_BINARY_OPERATOR(name, op) \
  39. OPAQUE_TYPE_BINARY_OPERATOR_T(name, op, TDef)
  40. #define OPAQUE_TYPE_MIXED_BINARY_OPERATOR(name, op) \
  41. template <typename TDef, typename RDef, typename Out> \
  42. struct name { \
  43. friend Out operator op(TDef const & lhs, RDef const & rhs) { \
  44. using type1 = underlying_type<TDef>; \
  45. using type2 = underlying_type<RDef>; \
  46. return Out(static_cast<type1 const&>(lhs) op static_cast<type2 const&>(rhs)); \
  47. } \
  48. }; \
  49. \
  50. template <typename TDef, typename RDef> \
  51. struct name<TDef, RDef, TDef> { \
  52. friend TDef operator op##=(TDef & lhs, RDef const & rhs) { \
  53. using type1 = underlying_type<TDef>; \
  54. using type2 = underlying_type<RDef>; \
  55. static_cast<type1 &>(lhs) op##= static_cast<type2 const&>(rhs); \
  56. return lhs; \
  57. } \
  58. \
  59. friend TDef operator op(TDef const & lhs, RDef const & rhs) { \
  60. using type1 = std::underlying_type<TDef>; \
  61. using type2 = std::underlying_type<RDef>; \
  62. return TDef(static_cast<type1 const&>(lhs) op static_cast<type2 const&>(rhs)); \
  63. } \
  64. }
  65. #define OPAQUE_TYPE_UNARY_OPERATOR(name, op) \
  66. template <typename TDef> \
  67. struct name { \
  68. friend TDef operator op(TDef const & val) { \
  69. using type = underlying_type<TDef>; \
  70. return TDef(op static_cast<type const&>(val)); \
  71. } \
  72. }
  73. OPAQUE_TYPE_UNARY_OPERATOR(unary_plus, +);
  74. OPAQUE_TYPE_UNARY_OPERATOR(unary_minus, -);
  75. OPAQUE_TYPE_ASSIGN_OPERATOR(addition, +);
  76. OPAQUE_TYPE_ASSIGN_OPERATOR(subtraction, -);
  77. OPAQUE_TYPE_ASSIGN_OPERATOR(multiplication, *);
  78. OPAQUE_TYPE_ASSIGN_OPERATOR(division, /);
  79. OPAQUE_TYPE_ASSIGN_OPERATOR(modulo, %);
  80. OPAQUE_TYPE_BINARY_OPERATOR_T(equals, ==, bool);
  81. OPAQUE_TYPE_BINARY_OPERATOR_T(not_equals, !=, bool);
  82. OPAQUE_TYPE_BINARY_OPERATOR_T(less_than, <, bool);
  83. OPAQUE_TYPE_BINARY_OPERATOR_T(less_equal, <=, bool);
  84. OPAQUE_TYPE_BINARY_OPERATOR_T(greater_than, >, bool);
  85. OPAQUE_TYPE_BINARY_OPERATOR_T(greater_equal, >=, bool);
  86. OPAQUE_TYPE_MIXED_BINARY_OPERATOR(mixed_addition, +);
  87. OPAQUE_TYPE_MIXED_BINARY_OPERATOR(mixed_subtraction, -);
  88. OPAQUE_TYPE_MIXED_BINARY_OPERATOR(mixed_multiplication, *);
  89. OPAQUE_TYPE_MIXED_BINARY_OPERATOR(mixed_division, /);
  90. OPAQUE_TYPE_ASSIGN_OPERATOR(bitwise_and, &);
  91. OPAQUE_TYPE_ASSIGN_OPERATOR(bitwise_or, |);
  92. OPAQUE_TYPE_ASSIGN_OPERATOR(exclusive_or, ^);
  93. #undef OPAQUE_TYPE_BINARY_OPERATOR_T
  94. #undef OPAQUE_TYPE_BINARY_OPERATOR
  95. #undef OPAQUE_TYPE_MIXED_BINARY_OPERATOR
  96. #undef OPAQUE_TYPE_UNARY_OPERATOR
  97. #undef OPAQUE_TYPE_ASSIGN_OPERATOR
  98. template <typename TDef>
  99. struct numeric_arithmetic : unary_plus<TDef>, unary_minus<TDef>, addition<TDef>, subtraction<TDef>, multiplication<TDef>, division<TDef> {};
  100. template <typename TDef>
  101. struct integer_arithmetic : numeric_arithmetic<TDef>, modulo<TDef> {};
  102. template <typename TDef>
  103. struct equality_comparible : equals<TDef>, not_equals<TDef> {};
  104. template <typename TDef>
  105. struct orderable : equality_comparible<TDef>, less_than<TDef>, less_equal<TDef>, greater_than<TDef>, greater_equal<TDef> {};
  106. template <typename Self, typename T>
  107. class opaque_typedef {
  108. public:
  109. opaque_typedef() : _value() {}
  110. explicit opaque_typedef(T const & val) : _value(val) {}
  111. explicit opaque_typedef(T && val) noexcept(std::is_nothrow_move_constructible<T>::value) : _value(std::move(val)) {}
  112. explicit operator T &() noexcept { return _value; }
  113. explicit operator T const&() const noexcept { return _value; }
  114. friend void swap(opaque_typedef & lhs, opaque_typedef & rhs) {
  115. using std::swap;
  116. swap(lhs._value, rhs._value);
  117. }
  118. protected:
  119. T const & self() const { return _value; }
  120. private:
  121. T _value;
  122. };
  123. #include <boost/preprocessor/variadic/to_seq.hpp>
  124. #include <boost/preprocessor/tuple/to_seq.hpp>
  125. #include <boost/preprocessor/seq/for_each.hpp>
  126. #define OPAQUE_TYPEDEF_CONVERT(r, x, type) operator type() const;
  127. #define OPAQUE_TYPEDEF_CONVERSIONS(...) \
  128. BOOST_PP_SEQ_FOR_EACH(OPAQUE_TYPEDEF_CONVERT, _, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
  129. }
  130. #define CREATE_CONVERTABLE_OPAQUE_TYPEDEF(name, basetype, ...) \
  131. struct name : opaque_typedef<name, basetype> \
  132. BOOST_PP_SEQ_FOR_EACH(OPAQUE_TYPEDEF_PARENT, name, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
  133. { \
  134. using opaque_typedef::opaque_typedef; \
  135. OPAQUE_TYPEDEF_CONVERSIONS
  136. #define OPAQUE_TYPEDEF_PARENT(r, name, parent) , parent < name >
  137. #define CREATE_OPAQUE_TYPEDEF(name, basetype, ...) \
  138. struct name : opaque_typedef<name, basetype> \
  139. BOOST_PP_SEQ_FOR_EACH(OPAQUE_TYPEDEF_PARENT, name, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
  140. { \
  141. using opaque_typedef::opaque_typedef; \
  142. }