diff --git a/src/operations/impl.cppm b/src/operations/impl.cppm index 338a3f3..60ac12d 100644 --- a/src/operations/impl.cppm +++ b/src/operations/impl.cppm @@ -4,14 +4,70 @@ import mcpplibs.primitives.operations.traits; export namespace mcpplibs::primitives::operations { +// Unary operations +struct Increment {}; +struct Decrement {}; +struct BitwiseNot {}; +struct UnaryPlus {}; +struct UnaryMinus {}; + +// Binary operations struct Addition {}; struct Subtraction {}; struct Multiplication {}; struct Division {}; +struct Modulus {}; +struct LeftShift {}; +struct RightShift {}; +struct BitwiseAnd {}; +struct BitwiseOr {}; +struct BitwiseXor {}; + +// Comparison operations struct Equal {}; struct NotEqual {}; struct ThreeWayCompare {}; +template <> struct traits { + using op_tag = Increment; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::unary; + static constexpr auto capability_mask = capability::arithmetic; +}; + +template <> struct traits { + using op_tag = Decrement; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::unary; + static constexpr auto capability_mask = capability::arithmetic; +}; + +template <> struct traits { + using op_tag = BitwiseNot; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::unary; + static constexpr auto capability_mask = capability::bitwise; +}; + +template <> struct traits { + using op_tag = UnaryPlus; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::unary; + static constexpr auto capability_mask = capability::arithmetic; +}; + +template <> struct traits { + using op_tag = UnaryMinus; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::unary; + static constexpr auto capability_mask = capability::arithmetic; +}; + template <> struct traits { using op_tag = Addition; @@ -44,6 +100,54 @@ template <> struct traits { static constexpr auto capability_mask = capability::arithmetic; }; +template <> struct traits { + using op_tag = Modulus; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::binary; + static constexpr auto capability_mask = capability::arithmetic; +}; + +template <> struct traits { + using op_tag = LeftShift; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::binary; + static constexpr auto capability_mask = capability::bitwise; +}; + +template <> struct traits { + using op_tag = RightShift; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::binary; + static constexpr auto capability_mask = capability::bitwise; +}; + +template <> struct traits { + using op_tag = BitwiseAnd; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::binary; + static constexpr auto capability_mask = capability::bitwise; +}; + +template <> struct traits { + using op_tag = BitwiseOr; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::binary; + static constexpr auto capability_mask = capability::bitwise; +}; + +template <> struct traits { + using op_tag = BitwiseXor; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::binary; + static constexpr auto capability_mask = capability::bitwise; +}; + template <> struct traits { using op_tag = Equal; diff --git a/src/operations/invoker.cppm b/src/operations/invoker.cppm index 569d04c..7c84208 100644 --- a/src/operations/invoker.cppm +++ b/src/operations/invoker.cppm @@ -194,6 +194,217 @@ constexpr auto checked_div(T lhs, T rhs) -> policy::value::decision { "checked division not supported for negotiated common type", lhs, rhs); } +template +constexpr auto checked_mod(T lhs, T rhs) -> policy::value::decision { + if constexpr (!std::is_integral_v) { + if constexpr (requires { lhs % rhs; }) { + policy::value::decision out{}; + out.has_value = true; + out.value = static_cast(lhs % rhs); + return out; + } + return make_error( + policy::error::kind::unspecified, + "checked modulus not supported for negotiated common type", lhs, rhs); + } else { + if (rhs == T{}) { + return make_error(policy::error::kind::divide_by_zero, + "checked modulus by zero", lhs, rhs); + } + + if constexpr (std::is_signed_v) { + auto const minv = std::numeric_limits::min(); + if (lhs == minv && rhs == static_cast(-1)) { + return make_error(policy::error::kind::overflow, + "checked modulus overflow", lhs, rhs); + } + } + + policy::value::decision out{}; + out.has_value = true; + out.value = static_cast(lhs % rhs); + return out; + } +} + +template +constexpr auto checked_unary_plus(T lhs) -> policy::value::decision { + policy::value::decision out{}; + if constexpr (requires { +lhs; }) { + out.has_value = true; + out.value = static_cast(+lhs); + return out; + } + return make_error(policy::error::kind::unspecified, + "checked unary plus not supported for negotiated " + "common type"); +} + +template +constexpr auto checked_unary_minus(T lhs) -> policy::value::decision { + if constexpr (std::is_integral_v && std::is_signed_v) { + auto const minv = std::numeric_limits::min(); + if (lhs == minv) { + return make_error(policy::error::kind::overflow, + "checked unary minus overflow", lhs); + } + } + + policy::value::decision out{}; + if constexpr (requires { -lhs; }) { + out.has_value = true; + out.value = static_cast(-lhs); + return out; + } + return make_error(policy::error::kind::unspecified, + "checked unary minus not supported for negotiated " + "common type"); +} + +template +constexpr auto checked_shift_left(T lhs, T rhs) -> policy::value::decision { + if constexpr (!std::is_integral_v || std::same_as) { + return make_error( + policy::error::kind::unspecified, + "checked left shift not supported for negotiated common type", lhs, + rhs); + } else { + using unsigned_t = std::make_unsigned_t; + constexpr auto bit_width = std::numeric_limits::digits; + + if constexpr (std::is_signed_v) { + if (rhs < T{}) { + return make_error(policy::error::kind::domain_error, + "checked left shift negative count", lhs, rhs); + } + } + + auto const shift = static_cast(rhs); + if (shift >= static_cast(bit_width)) { + return make_error(policy::error::kind::domain_error, + "checked left shift count out of range", lhs, rhs); + } + + if constexpr (std::is_signed_v) { + if (lhs < T{}) { + return make_error(policy::error::kind::domain_error, + "checked left shift negative lhs", lhs, rhs); + } + + auto const maxv = std::numeric_limits::max(); + if (lhs > static_cast(maxv >> shift)) { + return make_error(policy::error::kind::overflow, + "checked left shift overflow", lhs, rhs); + } + } + + policy::value::decision out{}; + out.has_value = true; + out.value = static_cast(lhs << shift); + return out; + } +} + +template +constexpr auto checked_shift_right(T lhs, T rhs) + -> policy::value::decision { + if constexpr (!std::is_integral_v || std::same_as) { + return make_error( + policy::error::kind::unspecified, + "checked right shift not supported for negotiated common type", lhs, + rhs); + } else { + using unsigned_t = std::make_unsigned_t; + constexpr auto bit_width = std::numeric_limits::digits; + + if constexpr (std::is_signed_v) { + if (rhs < T{}) { + return make_error(policy::error::kind::domain_error, + "checked right shift negative count", lhs, rhs); + } + } + + auto const shift = static_cast(rhs); + if (shift >= static_cast(bit_width)) { + return make_error(policy::error::kind::domain_error, + "checked right shift count out of range", lhs, rhs); + } + + policy::value::decision out{}; + out.has_value = true; + out.value = static_cast(lhs >> shift); + return out; + } +} + +template +constexpr auto checked_bit_and(T lhs, T rhs) -> policy::value::decision { + if constexpr (std::integral) { + policy::value::decision out{}; + out.has_value = true; + out.value = static_cast(lhs & rhs); + return out; + } + + return make_error( + policy::error::kind::unspecified, + "checked bitwise and not supported for negotiated common type", lhs, rhs); +} + +template +constexpr auto checked_bit_or(T lhs, T rhs) -> policy::value::decision { + if constexpr (std::integral) { + policy::value::decision out{}; + out.has_value = true; + out.value = static_cast(lhs | rhs); + return out; + } + + return make_error( + policy::error::kind::unspecified, + "checked bitwise or not supported for negotiated common type", lhs, rhs); +} + +template +constexpr auto checked_bit_xor(T lhs, T rhs) -> policy::value::decision { + if constexpr (std::integral) { + policy::value::decision out{}; + out.has_value = true; + out.value = static_cast(lhs ^ rhs); + return out; + } + + return make_error(policy::error::kind::unspecified, + "checked bitwise xor not supported for negotiated " + "common type", + lhs, rhs); +} + +template +constexpr auto checked_bit_not(T lhs) -> policy::value::decision { + if constexpr (std::integral) { + policy::value::decision out{}; + out.has_value = true; + out.value = static_cast(~lhs); + return out; + } + + return make_error(policy::error::kind::unspecified, + "checked bitwise not supported for negotiated " + "common type", + lhs); +} + +template +constexpr auto checked_inc(T lhs) -> policy::value::decision { + return checked_add(lhs, static_cast(1)); +} + +template +constexpr auto checked_dec(T lhs) -> policy::value::decision { + return checked_sub(lhs, static_cast(1)); +} + template constexpr auto compare_equal(T lhs, T rhs) -> policy::value::decision { policy::value::decision out{}; @@ -330,6 +541,143 @@ constexpr auto unchecked_div(T lhs, T rhs) -> policy::value::decision { return out; } +template +constexpr auto unchecked_mod(T lhs, T rhs) -> policy::value::decision { + policy::value::decision out{}; + out.has_value = true; + + if constexpr (requires { lhs % rhs; }) { + out.value = static_cast(lhs % rhs); + return out; + } + + out.value = T{}; + return out; +} + +template +constexpr auto unchecked_shift_left(T lhs, T rhs) -> policy::value::decision { + policy::value::decision out{}; + out.has_value = true; + + if constexpr (requires { lhs << rhs; }) { + out.value = static_cast(lhs << rhs); + return out; + } + + out.value = T{}; + return out; +} + +template +constexpr auto unchecked_shift_right(T lhs, T rhs) + -> policy::value::decision { + policy::value::decision out{}; + out.has_value = true; + + if constexpr (requires { lhs >> rhs; }) { + out.value = static_cast(lhs >> rhs); + return out; + } + + out.value = T{}; + return out; +} + +template +constexpr auto unchecked_bit_and(T lhs, T rhs) -> policy::value::decision { + policy::value::decision out{}; + out.has_value = true; + + if constexpr (requires { lhs & rhs; }) { + out.value = static_cast(lhs & rhs); + return out; + } + + out.value = T{}; + return out; +} + +template +constexpr auto unchecked_bit_or(T lhs, T rhs) -> policy::value::decision { + policy::value::decision out{}; + out.has_value = true; + + if constexpr (requires { lhs | rhs; }) { + out.value = static_cast(lhs | rhs); + return out; + } + + out.value = T{}; + return out; +} + +template +constexpr auto unchecked_bit_xor(T lhs, T rhs) -> policy::value::decision { + policy::value::decision out{}; + out.has_value = true; + + if constexpr (requires { lhs ^ rhs; }) { + out.value = static_cast(lhs ^ rhs); + return out; + } + + out.value = T{}; + return out; +} + +template +constexpr auto unchecked_bit_not(T lhs) -> policy::value::decision { + policy::value::decision out{}; + out.has_value = true; + + if constexpr (requires { ~lhs; }) { + out.value = static_cast(~lhs); + return out; + } + + out.value = T{}; + return out; +} + +template +constexpr auto unchecked_unary_plus(T lhs) -> policy::value::decision { + policy::value::decision out{}; + out.has_value = true; + + if constexpr (requires { +lhs; }) { + out.value = static_cast(+lhs); + return out; + } + + out.value = T{}; + return out; +} + +template +constexpr auto unchecked_unary_minus(T lhs) -> policy::value::decision { + policy::value::decision out{}; + out.has_value = true; + + if constexpr (requires { -lhs; }) { + out.value = static_cast(-lhs); + return out; + } + + out.value = T{}; + return out; +} + +template +constexpr auto unchecked_inc(T lhs) -> policy::value::decision { + return unchecked_add(lhs, static_cast(1)); +} + +template +constexpr auto unchecked_dec(T lhs) -> policy::value::decision { + return unchecked_sub(lhs, static_cast(1)); +} + template constexpr auto saturating_add(T lhs, T rhs) -> T { if constexpr (!std::is_integral_v || std::is_same_v) { return static_cast(lhs + rhs); @@ -409,6 +757,18 @@ template constexpr auto saturating_mul(T lhs, T rhs) -> T { } } +template constexpr auto saturating_unary_minus(T lhs) -> T { + return saturating_sub(static_cast(0), lhs); +} + +template constexpr auto saturating_inc(T lhs) -> T { + return saturating_add(lhs, static_cast(1)); +} + +template constexpr auto saturating_dec(T lhs) -> T { + return saturating_sub(lhs, static_cast(1)); +} + template constexpr auto make_unsupported(char const *reason) -> policy::value::decision { @@ -508,6 +868,131 @@ struct op_binding { } }; +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + if constexpr (requires { details::checked_mod(lhs, rhs); }) { + return details::checked_mod(lhs, rhs); + } + + return details::make_unsupported( + "checked modulus not supported for negotiated common type"); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + if constexpr (requires { details::checked_shift_left(lhs, rhs); }) { + return details::checked_shift_left(lhs, rhs); + } + + return details::make_unsupported( + "checked left shift not supported for negotiated common type"); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + if constexpr (requires { details::checked_shift_right(lhs, rhs); }) { + return details::checked_shift_right(lhs, rhs); + } + + return details::make_unsupported( + "checked right shift not supported for negotiated common type"); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return details::checked_bit_and(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return details::checked_bit_or(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return details::checked_bit_xor(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep) + -> policy::value::decision { + return details::checked_inc(lhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep) + -> policy::value::decision { + return details::checked_dec(lhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep) + -> policy::value::decision { + return details::checked_unary_plus(lhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep) + -> policy::value::decision { + return details::checked_unary_minus(lhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep) + -> policy::value::decision { + return details::checked_bit_not(lhs); + } +}; + template struct op_binding { static constexpr bool enabled = true; @@ -548,6 +1033,116 @@ struct op_binding { } }; +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return details::unchecked_mod(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return details::unchecked_shift_left(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return details::unchecked_shift_right(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return details::unchecked_bit_and(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return details::unchecked_bit_or(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return details::unchecked_bit_xor(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep) + -> policy::value::decision { + return details::unchecked_inc(lhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep) + -> policy::value::decision { + return details::unchecked_dec(lhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep) + -> policy::value::decision { + return details::unchecked_unary_plus(lhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep) + -> policy::value::decision { + return details::unchecked_unary_minus(lhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep) + -> policy::value::decision { + return details::unchecked_bit_not(lhs); + } +}; + template struct op_binding { static constexpr bool enabled = true; @@ -622,6 +1217,140 @@ struct op_binding { } }; +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return op_binding::apply(lhs, + rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return op_binding::apply(lhs, + rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return op_binding::apply( + lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return details::checked_bit_and(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return details::checked_bit_or(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return details::checked_bit_xor(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep) + -> policy::value::decision { + policy::value::decision out{}; + if constexpr (requires { details::saturating_inc(lhs); }) { + out.has_value = true; + out.value = details::saturating_inc(lhs); + return out; + } + return details::make_unsupported( + "saturating increment not supported for negotiated common type"); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep) + -> policy::value::decision { + policy::value::decision out{}; + if constexpr (requires { details::saturating_dec(lhs); }) { + out.has_value = true; + out.value = details::saturating_dec(lhs); + return out; + } + return details::make_unsupported( + "saturating decrement not supported for negotiated common type"); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep) + -> policy::value::decision { + return details::checked_unary_plus(lhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep) + -> policy::value::decision { + policy::value::decision out{}; + if constexpr (requires { details::saturating_unary_minus(lhs); }) { + out.has_value = true; + out.value = details::saturating_unary_minus(lhs); + return out; + } + return details::make_unsupported( + "saturating unary minus not supported for negotiated common type"); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep) + -> policy::value::decision { + return details::checked_bit_not(lhs); + } +}; + template struct op_binding { static constexpr bool enabled = true; diff --git a/src/operations/operators.cppm b/src/operations/operators.cppm index 3c04ca1..67f8b40 100644 --- a/src/operations/operators.cppm +++ b/src/operations/operators.cppm @@ -110,6 +110,16 @@ template , Primitive, ErrorPayload>; +template +using unary_dispatch_result_t = + primitive_dispatch_result_t; + +template +constexpr auto apply_assign(Lhs &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t; + template constexpr auto apply(Lhs const &lhs, Rhs const &rhs) @@ -126,6 +136,13 @@ constexpr auto apply(Lhs const &lhs, Rhs const &rhs) return result_primitive{*raw}; } +template +constexpr auto apply(Operand const &operand) + -> unary_dispatch_result_t { + return apply(operand, operand); +} + template constexpr auto apply(Lhs const &lhs, Rhs const &rhs) @@ -145,6 +162,40 @@ constexpr auto apply(Lhs const &lhs, Rhs const &rhs) return apply(bridge_lhs, rhs); } +// Unary operations +template +constexpr auto increment(Operand &operand) + -> unary_dispatch_result_t { + return apply_assign(operand, + operand); +} + +template +constexpr auto decrement(Operand &operand) + -> unary_dispatch_result_t { + return apply_assign(operand, + operand); +} + +template +constexpr auto bit_not(Operand const &operand) + -> unary_dispatch_result_t { + return apply(operand); +} + +template +constexpr auto unary_plus(Operand const &operand) + -> unary_dispatch_result_t { + return apply(operand); +} + +template +constexpr auto unary_minus(Operand const &operand) + -> unary_dispatch_result_t { + return apply(operand); +} + +// Binary arithmetic operations template constexpr auto add(Lhs const &lhs, Rhs const &rhs) @@ -219,6 +270,139 @@ constexpr auto div(Lhs const &lhs, Rhs const &rhs) return apply(lhs, rhs); } +template +constexpr auto mod(Lhs const &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto mod(Lhs const &lhs, Rhs const &rhs) + -> mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto mod(Lhs const &lhs, Rhs const &rhs) + -> flipped_mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +// Binary bitwise operations +template +constexpr auto shift_left(Lhs const &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto shift_left(Lhs const &lhs, Rhs const &rhs) + -> mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto shift_left(Lhs const &lhs, Rhs const &rhs) + -> flipped_mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto shift_right(Lhs const &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto shift_right(Lhs const &lhs, Rhs const &rhs) + -> mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto shift_right(Lhs const &lhs, Rhs const &rhs) + -> flipped_mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto bit_and(Lhs const &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto bit_and(Lhs const &lhs, Rhs const &rhs) + -> mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto bit_and(Lhs const &lhs, Rhs const &rhs) + -> flipped_mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto bit_or(Lhs const &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto bit_or(Lhs const &lhs, Rhs const &rhs) + -> mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto bit_or(Lhs const &lhs, Rhs const &rhs) + -> flipped_mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto bit_xor(Lhs const &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto bit_xor(Lhs const &lhs, Rhs const &rhs) + -> mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto bit_xor(Lhs const &lhs, Rhs const &rhs) + -> flipped_mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + template constexpr auto div(Lhs const &lhs, Rhs const &rhs) @@ -376,10 +560,106 @@ constexpr auto div_assign(Lhs &lhs, Rhs const &rhs) return apply_assign(lhs, rhs); } +template +constexpr auto mod_assign(Lhs &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply_assign(lhs, rhs); +} + +template +constexpr auto shift_left_assign(Lhs &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply_assign(lhs, rhs); +} + +template +constexpr auto shift_right_assign(Lhs &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply_assign(lhs, rhs); +} + +template +constexpr auto bit_and_assign(Lhs &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply_assign(lhs, rhs); +} + +template +constexpr auto bit_or_assign(Lhs &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply_assign(lhs, rhs); +} + +template +constexpr auto bit_xor_assign(Lhs &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply_assign(lhs, rhs); +} + } // namespace mcpplibs::primitives::operations export namespace mcpplibs::primitives::operators { +// Unary operators +template +constexpr auto operator++(Operand &operand) + -> operations::unary_dispatch_result_t { + return operations::increment(operand); +} + +template +constexpr auto operator++(Operand &operand, int) + -> operations::unary_dispatch_result_t { + auto const before = operand; + auto const stepped = operations::increment(operand); + if (!stepped.has_value()) { + return std::unexpected(stepped.error()); + } + return before; +} + +template +constexpr auto operator--(Operand &operand) + -> operations::unary_dispatch_result_t { + return operations::decrement(operand); +} + +template +constexpr auto operator--(Operand &operand, int) + -> operations::unary_dispatch_result_t { + auto const before = operand; + auto const stepped = operations::decrement(operand); + if (!stepped.has_value()) { + return std::unexpected(stepped.error()); + } + return before; +} + +template +constexpr auto operator+(Operand const &operand) + -> operations::unary_dispatch_result_t { + return operations::unary_plus(operand); +} + +template +constexpr auto operator-(Operand const &operand) + -> operations::unary_dispatch_result_t { + return operations::unary_minus(operand); +} + +template +constexpr auto operator~(Operand const &operand) + -> operations::unary_dispatch_result_t { + return operations::bit_not(operand); +} + +// Binary arithmetic operators template constexpr auto operator+(Lhs const &lhs, Rhs const &rhs) @@ -474,6 +754,147 @@ constexpr auto operator/(Lhs const &lhs, Rhs const &rhs) return operations::div(lhs, rhs); } +template +constexpr auto operator%(Lhs const &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::mod(lhs, rhs); +} + +template +constexpr auto operator%(Lhs const &lhs, Rhs const &rhs) + -> operations::mixed_primitive_dispatch_result_t { + return operations::mod(lhs, rhs); +} + +template +constexpr auto operator%(Lhs const &lhs, Rhs const &rhs) + -> operations::flipped_mixed_primitive_dispatch_result_t< + operations::Modulus, Lhs, Rhs> { + return operations::mod(lhs, rhs); +} + +// Binary bitwise operators +template +constexpr auto operator<<(Lhs const &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::shift_left(lhs, rhs); +} + +template +constexpr auto operator<<(Lhs const &lhs, Rhs const &rhs) + -> operations::mixed_primitive_dispatch_result_t { + return operations::shift_left(lhs, rhs); +} + +template +constexpr auto operator<<(Lhs const &lhs, Rhs const &rhs) + -> operations::flipped_mixed_primitive_dispatch_result_t< + operations::LeftShift, Lhs, Rhs> { + return operations::shift_left(lhs, rhs); +} + +template +constexpr auto operator>>(Lhs const &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::shift_right(lhs, rhs); +} + +template +constexpr auto operator>>(Lhs const &lhs, Rhs const &rhs) + -> operations::mixed_primitive_dispatch_result_t { + return operations::shift_right(lhs, rhs); +} + +template +constexpr auto operator>>(Lhs const &lhs, Rhs const &rhs) + -> operations::flipped_mixed_primitive_dispatch_result_t< + operations::RightShift, Lhs, Rhs> { + return operations::shift_right(lhs, rhs); +} + +template +constexpr auto operator&(Lhs const &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::bit_and(lhs, rhs); +} + +template +constexpr auto operator&(Lhs const &lhs, Rhs const &rhs) + -> operations::mixed_primitive_dispatch_result_t { + return operations::bit_and(lhs, rhs); +} + +template +constexpr auto operator&(Lhs const &lhs, Rhs const &rhs) + -> operations::flipped_mixed_primitive_dispatch_result_t< + operations::BitwiseAnd, Lhs, Rhs> { + return operations::bit_and(lhs, rhs); +} + +template +constexpr auto operator|(Lhs const &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::bit_or(lhs, rhs); +} + +template +constexpr auto operator|(Lhs const &lhs, Rhs const &rhs) + -> operations::mixed_primitive_dispatch_result_t { + return operations::bit_or(lhs, rhs); +} + +template +constexpr auto operator|(Lhs const &lhs, Rhs const &rhs) + -> operations::flipped_mixed_primitive_dispatch_result_t< + operations::BitwiseOr, Lhs, Rhs> { + return operations::bit_or(lhs, rhs); +} + +template +constexpr auto operator^(Lhs const &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::bit_xor(lhs, rhs); +} + +template +constexpr auto operator^(Lhs const &lhs, Rhs const &rhs) + -> operations::mixed_primitive_dispatch_result_t { + return operations::bit_xor(lhs, rhs); +} + +template +constexpr auto operator^(Lhs const &lhs, Rhs const &rhs) + -> operations::flipped_mixed_primitive_dispatch_result_t< + operations::BitwiseXor, Lhs, Rhs> { + return operations::bit_xor(lhs, rhs); +} + +// Binary comparison operators + template constexpr auto operator==(Lhs const &lhs, Rhs const &rhs) @@ -571,4 +992,46 @@ constexpr auto operator/=(Lhs &lhs, Rhs const &rhs) return operations::div_assign(lhs, rhs); } +template +constexpr auto operator%=(Lhs &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::mod_assign(lhs, rhs); +} + +template +constexpr auto operator<<=(Lhs &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::shift_left_assign(lhs, rhs); +} + +template +constexpr auto operator>>=(Lhs &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::shift_right_assign(lhs, rhs); +} + +template +constexpr auto operator&=(Lhs &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::bit_and_assign(lhs, rhs); +} + +template +constexpr auto operator|=(Lhs &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::bit_or_assign(lhs, rhs); +} + +template +constexpr auto operator^=(Lhs &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::bit_xor_assign(lhs, rhs); +} + } // namespace mcpplibs::primitives::operators diff --git a/tests/basic/test_operations.cpp b/tests/basic/test_operations.cpp index a2cd1ff..0b5e512 100644 --- a/tests/basic/test_operations.cpp +++ b/tests/basic/test_operations.cpp @@ -784,6 +784,120 @@ TEST(OperationsTest, OperatorPlusDelegatesToDispatcher) { EXPECT_EQ(result->value(), 15); } +TEST(OperationsTest, UnaryOperatorsDelegateToDispatcher) { + using namespace mcpplibs::primitives::operators; + using value_t = + primitive; + + auto value = value_t{10}; + + auto const inc = ++value; + ASSERT_TRUE(inc.has_value()); + EXPECT_EQ(inc->value(), 11); + EXPECT_EQ(value.load(), 11); + + auto const dec = --value; + ASSERT_TRUE(dec.has_value()); + EXPECT_EQ(dec->value(), 10); + EXPECT_EQ(value.load(), 10); + + auto const post_inc = value++; + ASSERT_TRUE(post_inc.has_value()); + EXPECT_EQ(post_inc->value(), 10); + EXPECT_EQ(value.load(), 11); + + auto const post_dec = value--; + ASSERT_TRUE(post_dec.has_value()); + EXPECT_EQ(post_dec->value(), 11); + EXPECT_EQ(value.load(), 10); + + auto const pos = +value; + auto const neg = -value; + auto const inv = ~value; + + ASSERT_TRUE(pos.has_value()); + ASSERT_TRUE(neg.has_value()); + ASSERT_TRUE(inv.has_value()); + EXPECT_EQ(pos->value(), 10); + EXPECT_EQ(neg->value(), -10); + EXPECT_EQ(inv->value(), ~10); +} + +TEST(OperationsTest, NewBinaryOperatorsDelegateToDispatcher) { + using namespace mcpplibs::primitives::operators; + using value_t = + primitive; + + auto const lhs = value_t{12}; + auto const rhs = value_t{5}; + + auto const mod = lhs % rhs; + auto const shl = rhs << value_t{1}; + auto const shr = lhs >> value_t{2}; + auto const bit_and = lhs & value_t{10}; + auto const bit_or = lhs | value_t{10}; + auto const bit_xor = lhs ^ value_t{10}; + + ASSERT_TRUE(mod.has_value()); + ASSERT_TRUE(shl.has_value()); + ASSERT_TRUE(shr.has_value()); + ASSERT_TRUE(bit_and.has_value()); + ASSERT_TRUE(bit_or.has_value()); + ASSERT_TRUE(bit_xor.has_value()); + EXPECT_EQ(mod->value(), 2); + EXPECT_EQ(shl->value(), 10); + EXPECT_EQ(shr->value(), 3); + EXPECT_EQ(bit_and->value(), 8); + EXPECT_EQ(bit_or->value(), 14); + EXPECT_EQ(bit_xor->value(), 6); +} + +TEST(OperationsTest, NewCompoundAssignmentOperatorsMutateLhsOnSuccess) { + using namespace mcpplibs::primitives::operators; + using value_t = + primitive; + + auto value = value_t{12}; + + auto mod = (value %= value_t{5}); + ASSERT_TRUE(mod.has_value()); + EXPECT_EQ(value.load(), 2); + + auto shl = (value <<= value_t{2}); + ASSERT_TRUE(shl.has_value()); + EXPECT_EQ(value.load(), 8); + + auto shr = (value >>= value_t{1}); + ASSERT_TRUE(shr.has_value()); + EXPECT_EQ(value.load(), 4); + + auto bit_and = (value &= value_t{6}); + ASSERT_TRUE(bit_and.has_value()); + EXPECT_EQ(value.load(), 4); + + auto bit_or = (value |= value_t{1}); + ASSERT_TRUE(bit_or.has_value()); + EXPECT_EQ(value.load(), 5); + + auto bit_xor = (value ^= value_t{7}); + ASSERT_TRUE(bit_xor.has_value()); + EXPECT_EQ(value.load(), 2); +} + +TEST(OperationsTest, ModulusAndShiftReportExpectedErrors) { + using value_t = + primitive; + + auto const mod_zero = operations::mod(value_t{7}, value_t{0}); + ASSERT_FALSE(mod_zero.has_value()); + EXPECT_EQ(mod_zero.error(), policy::error::kind::divide_by_zero); + + auto const invalid_shift = operations::shift_left( + value_t{1}, value_t{std::numeric_limits::digits + 1}); + ASSERT_FALSE(invalid_shift.has_value()); + EXPECT_EQ(invalid_shift.error(), policy::error::kind::domain_error); +} + TEST(OperationsTest, ThreeWayCompareReturnsStrongOrderingForIntegers) { using namespace mcpplibs::primitives::operators; using value_t = diff --git a/tests/basic/test_templates.cpp b/tests/basic/test_underlying.cpp similarity index 100% rename from tests/basic/test_templates.cpp rename to tests/basic/test_underlying.cpp