opaque_typedef.hpp 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  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. template <typename Tag, typename T>
  11. T underlying_type_impl(strong_typedef<Tag, T>);
  12. template <typename T>
  13. using underlying_type = decltype(underlying_type_impl(std::declval<T>()));
  14. #define OPAQUE_TYPE_ASSIGN_OPERATOR(name, op) \
  15. template <typename TDef> \
  16. struct name { \
  17. friend TDef & operator op##=(TDef & lhs, TDef const & rhs) { \
  18. using type = underlying_type<TDef>; \
  19. static_cast<type &>(lhs) op##= static_cast<type const &>(rhs); \
  20. return lhs; \
  21. } \
  22. \
  23. friend TDef operator op(TDef const & lhs, TDef const & rhs) { \
  24. using type = underlying_type<TDef>; \
  25. return TDef(static_cast<type const&>(lhs) op static_cast<type const&>(rhs)); \
  26. } \
  27. }
  28. #define OPAQUE_TYPE_BINARY_OPERATOR(name, op) \
  29. template <typename TDef> \
  30. struct name { \
  31. friend TDef operator op(TDef const & lhs, TDef const & rhs) { \
  32. using type = underlying_type<TDef>; \
  33. return TDef(static_cast<type const&>(lhs) op static_cast<type const&>(rhs)); \
  34. } \
  35. }
  36. #define OPAQUE_TYPE_MIXED_BINARY_OPERATOR(name, op) \
  37. template <typename TDef, typename RDef, typename Out> \
  38. struct name { \
  39. friend Out operator op(TDef const & lhs, RDef const & rhs) { \
  40. using type1 = underlying_type<TDef>; \
  41. using type2 = underlying_type<RDef>; \
  42. return Out(static_cast<type1 const&>(lhs) op static_cast<type2 const&>(rhs)); \
  43. } \
  44. }; \
  45. \
  46. template <typename TDef, typename RDef> \
  47. struct name<TDef, RDef, TDef> { \
  48. friend TDef operator op##=(TDef & lhs, RDef const & rhs) { \
  49. using type1 = underlying_type<TDef>; \
  50. using type2 = underlying_type<RDef>; \
  51. static_cast<type1 &>(lhs) op##= static_cast<type2 const&>(rhs); \
  52. return lhs; \
  53. } \
  54. \
  55. friend Out operator op(TDef const & lhs, RDef const & rhs) { \
  56. using type1 = underlying_type<TDef>; \
  57. using type2 = underlying_type<RDef>; \
  58. return Out(static_cast<type1 const&>(lhs) op static_cast<type2 const&>(rhs)); \
  59. } \
  60. }
  61. #define OPAQUE_TYPE_BINARY_OPERATOR(name, op) \
  62. template <typename TDef> \
  63. struct name { \
  64. friend TDef operator op(TDef const & val) { \
  65. using type = underlying_type<TDef>; \
  66. return TDef(op static_cast<type const&>(val)); \
  67. } \
  68. }
  69. OPAQUE_TYPE_UNARY_OPERATOR(unary_plus, +);
  70. OPAQUE_TYPE_UNARY_OPERATOR(unary_minus, -);
  71. OPAQUE_TYPE_ASSIGN_OPERATOR(addition, +);
  72. OPAQUE_TYPE_ASSIGN_OPERATOR(subtraction, -);
  73. OPAQUE_TYPE_ASSIGN_OPERATOR(multiplication, *);
  74. OPAQUE_TYPE_ASSIGN_OPERATOR(division, /);
  75. OPAQUE_TYPE_ASSIGN_OPERATOR(modulo, %);
  76. OPAQUE_TYPE_MIXED_BINARY_OPERATOR(mixed_addition, +);
  77. OPAQUE_TYPE_MIXED_BINARY_OPERATOR(mixed_subtraction, -);
  78. OPAQUE_TYPE_MIXED_BINARY_OPERATOR(mixed_multiplication, *);
  79. OPAQUE_TYPE_MIXED_BINARY_OPERATOR(mixed_division, /);
  80. OPAQUE_TYPE_ASSIGN_OPERATOR(bitwise_and, &);
  81. OPAQUE_TYPE_ASSIGN_OPERATOR(bitwise_or, |);
  82. OPAQUE_TYPE_ASSIGN_OPERATOR(exclusive_or, ^);
  83. template <typename TDef>
  84. struct numeric_arithmatic : unary_plus<TDef>, unary_minus<TDef>, addition<TDef>, subtraction<TDef>, multiplication<TDef>, division<TDef> {};
  85. template <typename TDef>
  86. struct integer_arithmatic : numeric_arithmatic<TDef>, modulo<TDef> {}
  87. #undef OPAQUE_TYPE_BINARY_OPERATOR
  88. #undef OPAQUE_TYPE_MIXED_BINARY_OPERATOR
  89. #undef OPAQUE_TYPE_UNARY_OPERATOR
  90. #undef OPAQUE_TYPE_ASSIGN_OPERATOR
  91. template <typename Self, typename T>
  92. class opaque_typedef {
  93. public:
  94. opaque_typedef() : _value() {}
  95. explicit opaque_typedef(T const & val) : _value(val) {}
  96. explicit opaque_typedef(T && val) noexcept(std::is_nothrow_move_constructible<T>::value) : _value(std::move(val)) {}
  97. explicit operator T &() noexcept { return _value; }
  98. explicit operator T const&() const noexcept { return _value; }
  99. friend void swap(opaque_typedef & lhs, opaque_typedef & rhs) {
  100. using std::swap;
  101. swap(lhs._value, rhs._value);
  102. }
  103. private:
  104. T _value;
  105. };