Browse Source

Updating opaque typedef to resemble the strategy used in the foonathan blogpost credited.

Samuel Jaffe 9 years ago
parent
commit
74330c8727
1 changed files with 95 additions and 22 deletions
  1. 95 22
      opaque_typedef.hpp

+ 95 - 22
opaque_typedef.hpp

@@ -2,47 +2,120 @@
 //  opaque_typedef.hpp
 //  opaque-type
 //
+//  Inspired by https://foonathan.github.io/blog/2016/10/19/strong-typedefs.html and others
+//
 //  Created by Sam Jaffe on 8/16/16.
 //
 
 #pragma once
 
-#define OPAQUE_TYPE_OPERATOR(name, op) \
-  template <typename R, typename O1 = R, typename O2 = R> struct name; \
-  \
-  template <typename R, typename O1, typename O2> \
+template <typename Tag, typename T>
+T underlying_type_impl(strong_typedef<Tag, T>);
+
+template <typename T>
+using underlying_type = decltype(underlying_type_impl(std::declval<T>()));
+
+#define OPAQUE_TYPE_ASSIGN_OPERATOR(name, op) \
+  template <typename TDef> \
+  struct name { \
+    friend TDef & operator op##=(TDef & lhs, TDef const & rhs) { \
+      using type = underlying_type<TDef>; \
+      static_cast<type &>(lhs) op##= static_cast<type const &>(rhs); \
+      return lhs; \
+    } \
+    \
+    friend TDef operator op(TDef const & lhs, TDef const & rhs) { \
+      using type = underlying_type<TDef>; \
+      return TDef(static_cast<type const&>(lhs) op static_cast<type const&>(rhs)); \
+    } \
+  }
+
+#define OPAQUE_TYPE_BINARY_OPERATOR(name, op) \
+  template <typename TDef> \
+  struct name { \
+    friend TDef operator op(TDef const & lhs, TDef const & rhs) { \
+      using type = underlying_type<TDef>; \
+      return TDef(static_cast<type const&>(lhs) op static_cast<type const&>(rhs)); \
+    } \
+  }
+
+#define OPAQUE_TYPE_MIXED_BINARY_OPERATOR(name, op) \
+  template <typename TDef, typename RDef, typename Out> \
   struct name { \
-    friend R operator op(O1 const & lhs, O2 const & rhs) { \
-      return R(lhs.value() op rhs.value()); \
+    friend Out operator op(TDef const & lhs, RDef const & rhs) { \
+      using type1 = underlying_type<TDef>; \
+      using type2 = underlying_type<RDef>; \
+      return Out(static_cast<type1 const&>(lhs) op static_cast<type2 const&>(rhs)); \
     } \
   }; \
   \
-  template <typename R, typename O2> \
-  struct name<R, R, O2> { \
-    friend R & operator op##=(R & lhs, O2 const & rhs) { \
-      return lhs = R(lhs.value() op rhs.value()); \
+  template <typename TDef, typename RDef> \
+  struct name<TDef, RDef, TDef> { \
+    friend TDef operator op##=(TDef & lhs, RDef const & rhs) { \
+      using type1 = underlying_type<TDef>; \
+      using type2 = underlying_type<RDef>; \
+      static_cast<type1 &>(lhs) op##= static_cast<type2 const&>(rhs); \
+      return lhs; \
     } \
     \
-    friend R operator op(R const & lhs, O2 const & rhs) { \
-      return R(lhs.value() op rhs.value()); \
+    friend Out operator op(TDef const & lhs, RDef const & rhs) { \
+      using type1 = underlying_type<TDef>; \
+      using type2 = underlying_type<RDef>; \
+      return Out(static_cast<type1 const&>(lhs) op static_cast<type2 const&>(rhs)); \
+    } \
+  }
+
+#define OPAQUE_TYPE_BINARY_OPERATOR(name, op) \
+  template <typename TDef> \
+  struct name { \
+    friend TDef operator op(TDef const & val) { \
+      using type = underlying_type<TDef>; \
+      return TDef(op static_cast<type const&>(val)); \
     } \
-  };
+  }
 
-OPAQUE_TYPE_OPERATOR(addition, +)
-OPAQUE_TYPE_OPERATOR(subtraction, -)
-OPAQUE_TYPE_OPERATOR(multiplication, *)
-OPAQUE_TYPE_OPERATOR(division, /)
+OPAQUE_TYPE_UNARY_OPERATOR(unary_plus, +);
+OPAQUE_TYPE_UNARY_OPERATOR(unary_minus, -);
 
-#undef OPAQUE_TYPE_OPERATOR
+OPAQUE_TYPE_ASSIGN_OPERATOR(addition, +);
+OPAQUE_TYPE_ASSIGN_OPERATOR(subtraction, -);
+OPAQUE_TYPE_ASSIGN_OPERATOR(multiplication, *);
+OPAQUE_TYPE_ASSIGN_OPERATOR(division, /);
+OPAQUE_TYPE_ASSIGN_OPERATOR(modulo, %);
 
-template <typename T>
+OPAQUE_TYPE_MIXED_BINARY_OPERATOR(mixed_addition, +);
+OPAQUE_TYPE_MIXED_BINARY_OPERATOR(mixed_subtraction, -);
+OPAQUE_TYPE_MIXED_BINARY_OPERATOR(mixed_multiplication, *);
+OPAQUE_TYPE_MIXED_BINARY_OPERATOR(mixed_division, /);
+
+OPAQUE_TYPE_ASSIGN_OPERATOR(bitwise_and, &);
+OPAQUE_TYPE_ASSIGN_OPERATOR(bitwise_or, |);
+OPAQUE_TYPE_ASSIGN_OPERATOR(exclusive_or, ^);
+
+template <typename TDef>
+struct numeric_arithmatic : unary_plus<TDef>, unary_minus<TDef>, addition<TDef>, subtraction<TDef>, multiplication<TDef>, division<TDef> {};
+
+template <typename TDef>
+struct integer_arithmatic : numeric_arithmatic<TDef>, modulo<TDef> {}
+
+#undef OPAQUE_TYPE_BINARY_OPERATOR
+#undef OPAQUE_TYPE_MIXED_BINARY_OPERATOR
+#undef OPAQUE_TYPE_UNARY_OPERATOR
+#undef OPAQUE_TYPE_ASSIGN_OPERATOR
+
+template <typename Self, typename T>
 class opaque_typedef {
 public:
   opaque_typedef() : _value() {}
-  explicit opaque_typedef(T val) : _value(val) {}
-  explicit operator T() const { return _value; }
+  explicit opaque_typedef(T const & val) : _value(val) {}
+  explicit opaque_typedef(T && val) noexcept(std::is_nothrow_move_constructible<T>::value) : _value(std::move(val)) {}
+  explicit operator T &() noexcept { return _value; }
+  explicit operator T const&() const noexcept { return _value; }
   
-  T value() const { return _value; }
+  friend void swap(opaque_typedef & lhs, opaque_typedef & rhs) {
+    using std::swap;
+    swap(lhs._value, rhs._value);
+  }
 private:
   T _value;
 };