Skip to content

Commit

Permalink
Add noncommutative mixed operators
Browse files Browse the repository at this point in the history
Subtraction and division are noncommutative ((a - b) != (b - a)), and when mixing
different types you might not want these operators to work both ways.
This commit adds mixed_[operator]_noncommutative, which does _not_ declare the
operator overloads with the strong_typedef as the right hand side and the
other (mixed) type as the left hand side

Example:

struct year : strong_typedef<year, int>,
              mixed_subtraction_noncommutative<year, int> {};
year y(2018);
y - 3; // OK
3 - y; // compile error, operator not found.
  • Loading branch information
gerboengels committed Mar 29, 2018
1 parent 9db40a3 commit c198661
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 3 deletions.
20 changes: 17 additions & 3 deletions include/type_safe/strong_typedef.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ namespace type_safe
}

/// \exclude
#define TYPE_SAFE_DETAIL_MAKE_OP_MIXED(Op, Name, Result) \
#define TYPE_SAFE_DETAIL_MAKE_OP_STRONGTYPEDEF_OTHER(Op, Name, Result) \
/** \exclude */ \
template <class StrongTypedef, typename OtherArg, typename Other, \
typename = detail::enable_if_convertible_same<Other&&, OtherArg>> \
Expand All @@ -302,7 +302,9 @@ namespace type_safe
{ \
return Result(get(static_cast<StrongTypedef&&>(lhs)) \
Op detail::forward_or_underlying(detail::forward<Other>(rhs))); \
} \
}

#define TYPE_SAFE_DETAIL_MAKE_OP_OTHER_STRONGTYPEDEF(Op, Name, Result) \
/** \exclude */ \
template <class StrongTypedef, typename OtherArg, typename Other, \
typename = detail::enable_if_convertible_same<Other&&, OtherArg>> \
Expand All @@ -320,6 +322,10 @@ namespace type_safe
Op get(static_cast<StrongTypedef&&>(rhs))); \
}

#define TYPE_SAFE_DETAIL_MAKE_OP_MIXED(Op, Name, Result) \
TYPE_SAFE_DETAIL_MAKE_OP_STRONGTYPEDEF_OTHER(Op, Name, Result) \
TYPE_SAFE_DETAIL_MAKE_OP_OTHER_STRONGTYPEDEF(Op, Name, Result)

/// \exclude
#define TYPE_SAFE_DETAIL_MAKE_OP_COMPOUND(Op, Name) \
/** \exclude */ \
Expand Down Expand Up @@ -412,7 +418,13 @@ namespace type_safe
{ \
}; \
TYPE_SAFE_DETAIL_MAKE_OP_MIXED(Op, mixed_##Name, StrongTypedef) \
TYPE_SAFE_DETAIL_MAKE_OP_COMPOUND_MIXED(Op## =, mixed_##Name)
TYPE_SAFE_DETAIL_MAKE_OP_COMPOUND_MIXED(Op## =, mixed_##Name) \
template <class StrongTypedef, typename Other> \
struct mixed_##Name##_noncommutative \
{ \
}; \
TYPE_SAFE_DETAIL_MAKE_OP_STRONGTYPEDEF_OTHER(Op, mixed_##Name##_noncommutative, StrongTypedef) \
TYPE_SAFE_DETAIL_MAKE_OP_COMPOUND_MIXED(Op## =, mixed_##Name##_noncommutative)

template <class StrongTypedef>
struct equality_comparison
Expand Down Expand Up @@ -755,6 +767,8 @@ namespace type_safe

#undef TYPE_SAFE_DETAIL_MAKE_OP
#undef TYPE_SAFE_DETAIL_MAKE_OP_MIXED
#undef TYPE_SAFE_DETAIL_MAKE_OP_STRONGTYPEDEF_OTHER
#undef TYPE_SAFE_DETAIL_MAKE_OP_OTHER_STRONGTYPEDEF
#undef TYPE_SAFE_DETAIL_MAKE_OP_COMPOUND
#undef TYPE_SAFE_DETAIL_MAKE_STRONG_TYPEDEF_OP
} // namespace strong_typedef_op
Expand Down
45 changes: 45 additions & 0 deletions test/strong_typedef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,28 @@ TEST_CASE("strong_typedef")
b = 1 - b;
REQUIRE(static_cast<int>(b) == 3);
}
SECTION("subtraction noncommutative")
{
struct type : strong_typedef<type, int>,
strong_typedef_op::subtraction<type>,
strong_typedef_op::mixed_subtraction_noncommutative<type, int>
{
using strong_typedef::strong_typedef;
};

type a(0);
a -= type(1); // -1
a = a - type(1); // -2
a = type(1) - a; // 3
REQUIRE(static_cast<int>(a) == 3);

type b(0);
b -= 1;
b = b - 1;
REQUIRE(static_cast<int>(b) == -2);
static_assert(is_operator_minus_callable_with<type, int>::value, "");
static_assert(!is_operator_minus_callable_with<int, type>::value, "type is noncommutative");
}
SECTION("multiplication")
{
struct type : strong_typedef<type, int>,
Expand Down Expand Up @@ -338,6 +360,29 @@ TEST_CASE("strong_typedef")
b = 2 / b;
REQUIRE(static_cast<int>(b) == 1);
}
SECTION("division noncommutative")
{
struct type : strong_typedef<type, int>,
strong_typedef_op::division<type>,
strong_typedef_op::mixed_division_noncommutative<type, int>
{
using strong_typedef::strong_typedef;
};

type a(8);
a /= type(2);
a = a / type(2);
a = type(2) / a;
REQUIRE(static_cast<int>(a) == 1);

type b(8);
b /= 2;
b = b / 2;
REQUIRE(static_cast<int>(b) == 2);
static_assert(is_division_callable_with<type, int>::value, "");
static_assert(!is_division_callable_with<int, type>::value,
"division of type and int is noncommutative");
}
SECTION("modulo")
{
struct type : strong_typedef<type, int>,
Expand Down

0 comments on commit c198661

Please sign in to comment.