|
|
@@ -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;
|
|
|
};
|