// // 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 template T underlying_type_impl(strong_typedef); template using underlying_type = decltype(underlying_type_impl(std::declval())); #define OPAQUE_TYPE_ASSIGN_OPERATOR(name, op) \ template \ struct name { \ friend TDef & operator op##=(TDef & lhs, TDef const & rhs) { \ using type = underlying_type; \ static_cast(lhs) op##= static_cast(rhs); \ return lhs; \ } \ \ friend TDef operator op(TDef const & lhs, TDef const & rhs) { \ using type = underlying_type; \ return TDef(static_cast(lhs) op static_cast(rhs)); \ } \ } #define OPAQUE_TYPE_BINARY_OPERATOR(name, op) \ template \ struct name { \ friend TDef operator op(TDef const & lhs, TDef const & rhs) { \ using type = underlying_type; \ return TDef(static_cast(lhs) op static_cast(rhs)); \ } \ } #define OPAQUE_TYPE_MIXED_BINARY_OPERATOR(name, op) \ template \ struct name { \ friend Out operator op(TDef const & lhs, RDef const & rhs) { \ using type1 = underlying_type; \ using type2 = underlying_type; \ return Out(static_cast(lhs) op static_cast(rhs)); \ } \ }; \ \ template \ struct name { \ friend TDef operator op##=(TDef & lhs, RDef const & rhs) { \ using type1 = underlying_type; \ using type2 = underlying_type; \ static_cast(lhs) op##= static_cast(rhs); \ return lhs; \ } \ \ friend Out operator op(TDef const & lhs, RDef const & rhs) { \ using type1 = underlying_type; \ using type2 = underlying_type; \ return Out(static_cast(lhs) op static_cast(rhs)); \ } \ } #define OPAQUE_TYPE_BINARY_OPERATOR(name, op) \ template \ struct name { \ friend TDef operator op(TDef const & val) { \ using type = underlying_type; \ return TDef(op static_cast(val)); \ } \ } OPAQUE_TYPE_UNARY_OPERATOR(unary_plus, +); OPAQUE_TYPE_UNARY_OPERATOR(unary_minus, -); 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, %); 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 struct numeric_arithmatic : unary_plus, unary_minus, addition, subtraction, multiplication, division {}; template struct integer_arithmatic : numeric_arithmatic, modulo {} #undef OPAQUE_TYPE_BINARY_OPERATOR #undef OPAQUE_TYPE_MIXED_BINARY_OPERATOR #undef OPAQUE_TYPE_UNARY_OPERATOR #undef OPAQUE_TYPE_ASSIGN_OPERATOR template class opaque_typedef { public: opaque_typedef() : _value() {} explicit opaque_typedef(T const & val) : _value(val) {} explicit opaque_typedef(T && val) noexcept(std::is_nothrow_move_constructible::value) : _value(std::move(val)) {} explicit operator T &() noexcept { return _value; } explicit operator T const&() const noexcept { return _value; } friend void swap(opaque_typedef & lhs, opaque_typedef & rhs) { using std::swap; swap(lhs._value, rhs._value); } private: T _value; };