// // 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 #include template class opaque_typedef; template T underlying_type_impl(opaque_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_T(name, op, Out) \ template \ struct name { \ friend Out operator op(TDef const & lhs, TDef const & rhs) { \ using type = underlying_type; \ return Out(static_cast(lhs) op static_cast(rhs)); \ } \ } #define OPAQUE_TYPE_BINARY_OPERATOR(name, op) \ OPAQUE_TYPE_BINARY_OPERATOR_T(name, op, TDef) #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 TDef operator op(TDef const & lhs, RDef const & rhs) { \ using type1 = std::underlying_type; \ using type2 = std::underlying_type; \ return TDef(static_cast(lhs) op static_cast(rhs)); \ } \ } #define OPAQUE_TYPE_UNARY_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_BINARY_OPERATOR_T(equals, ==, bool); OPAQUE_TYPE_BINARY_OPERATOR_T(not_equals, !=, bool); OPAQUE_TYPE_BINARY_OPERATOR_T(less_than, <, bool); OPAQUE_TYPE_BINARY_OPERATOR_T(less_equal, <=, bool); OPAQUE_TYPE_BINARY_OPERATOR_T(greater_than, >, bool); OPAQUE_TYPE_BINARY_OPERATOR_T(greater_equal, >=, bool); 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, ^); #undef OPAQUE_TYPE_BINARY_OPERATOR_T #undef OPAQUE_TYPE_BINARY_OPERATOR #undef OPAQUE_TYPE_MIXED_BINARY_OPERATOR #undef OPAQUE_TYPE_UNARY_OPERATOR #undef OPAQUE_TYPE_ASSIGN_OPERATOR template struct numeric_arithmetic : unary_plus, unary_minus, addition, subtraction, multiplication, division {}; template struct integer_arithmetic : numeric_arithmetic, modulo {}; template struct equality_comparible : equals, not_equals {}; template struct orderable : equality_comparible, less_than, less_equal, greater_than, greater_equal {}; 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); } protected: T const & self() const { return _value; } private: T _value; }; #include #include #include #define OPAQUE_TYPEDEF_CONVERT(r, x, type) operator type() const; #define OPAQUE_TYPEDEF_CONVERSIONS(...) \ BOOST_PP_SEQ_FOR_EACH(OPAQUE_TYPEDEF_CONVERT, _, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ } #define CREATE_CONVERTABLE_OPAQUE_TYPEDEF(name, basetype, ...) \ struct name : opaque_typedef \ BOOST_PP_SEQ_FOR_EACH(OPAQUE_TYPEDEF_PARENT, name, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ { \ using opaque_typedef::opaque_typedef; \ OPAQUE_TYPEDEF_CONVERSIONS #define OPAQUE_TYPEDEF_PARENT(r, name, parent) , parent < name > #define CREATE_OPAQUE_TYPEDEF(name, basetype, ...) \ struct name : opaque_typedef \ BOOST_PP_SEQ_FOR_EACH(OPAQUE_TYPEDEF_PARENT, name, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \ { \ using opaque_typedef::opaque_typedef; \ }