diff --git a/detail/default_eval.hpp b/detail/default_eval.hpp index 74284f8..12d8236 100644 --- a/detail/default_eval.hpp +++ b/detail/default_eval.hpp @@ -130,6 +130,15 @@ namespace boost::proto17 { BOOST_PROTO17_BINARY_OPERATOR_CASE(bitwise_xor_assign) // ^= BOOST_PROTO17_BINARY_OPERATOR_CASE(subscript) // [] + else if constexpr (kind == expr_kind::if_else) { + return + eval_if_else( + default_eval_expr(expr.elements[0_c], static_cast(args)...), + default_eval_expr(expr.elements[1_c], static_cast(args)...), + default_eval_expr(expr.elements[2_c], static_cast(args)...) + ); + } + #undef BOOST_PROTO17_BINARY_OPERATOR_CASE else if constexpr (kind == expr_kind::call) { diff --git a/detail/expression.hpp b/detail/expression.hpp index b5b8295..c396c74 100644 --- a/detail/expression.hpp +++ b/detail/expression.hpp @@ -242,6 +242,95 @@ namespace boost::proto17 { >::type; + // ternary_op_result + + template < + template class ExprTemplate, + typename T, + typename U, + typename V, + bool Valid = is_expr::value || is_expr::value || is_expr::value + > + struct ternary_op_result; + + template < + template class ExprTemplate, + typename T, + typename U, + typename V + > + struct ternary_op_result + { + using cond_type = operand_type_t; + using then_type = operand_type_t; + using else_type = operand_type_t; + using type = ExprTemplate< + expr_kind::if_else, + hana::tuple + >; + }; + + template < + template class ExprTemplate, + typename T, + typename U, + typename V + > + using ternary_op_result_t = typename ternary_op_result::type; + + + // udt_any_ternary_op_result + + template < + template class ExprTemplate, + typename T, + typename U, + typename V, + template class UdtTrait, + bool Valid = + !is_expr::value && !is_expr::value && !is_expr::value && + ( + UdtTrait>::value || + UdtTrait>::value || + UdtTrait>::value + ) + > + struct udt_any_ternary_op_result; + + template < + template class ExprTemplate, + typename T, + typename U, + typename V, + template class UdtTrait + > + struct udt_any_ternary_op_result + { + using cond_type = operand_type_t; + using then_type = operand_type_t; + using else_type = operand_type_t; + using type = ExprTemplate< + expr_kind::if_else, + hana::tuple + >; + }; + + template < + template class ExprTemplate, + typename T, + typename U, + typename V, + template class UdtTrait + > + using udt_any_ternary_op_result_t = typename udt_any_ternary_op_result< + ExprTemplate, + T, + U, + V, + UdtTrait + >::type; + + // udt_unary_op_result template < @@ -339,8 +428,7 @@ namespace boost::proto17 { typename U, template class UdtTrait, bool Valid = - !is_expr::value && - !is_expr::value && + !is_expr::value && !is_expr::value && (UdtTrait>::value || UdtTrait>::value) > struct udt_any_binary_op_result; @@ -381,6 +469,7 @@ namespace boost::proto17 { invalid, one, two, + three, n }; @@ -441,6 +530,10 @@ namespace boost::proto17 { case expr_kind::subscript: // [] return expr_arity::two; + // ternary + case expr_kind::if_else: // (analogous to) ?: + return expr_arity::three; + // n-ary case expr_kind::call: // () return expr_arity::n; @@ -509,6 +602,9 @@ namespace boost::proto17 { CASE(bitwise_xor_assign); // ^= CASE(subscript); // [] + // ternary + CASE(if_else); // (analogous to) ?: + // n-ary CASE(call); // () diff --git a/expression.hpp b/expression.hpp index 9abc500..baac859 100644 --- a/expression.hpp +++ b/expression.hpp @@ -80,6 +80,8 @@ namespace boost::proto17 { case expr_kind::bitwise_xor_assign: return "^="; case expr_kind::subscript: return "[]"; + case expr_kind::if_else: return "?:"; + case expr_kind::call: return "()"; default: return "** ERROR: UNKNOWN OPERATOR! **"; @@ -407,6 +409,8 @@ namespace boost::proto17 { #undef BOOST_PROTO17_BINARY_FREE_OPERATOR + BOOST_PROTO17_USER_EXPR_IF_ELSE(::boost::proto17::expression) + template auto make_expression (T &&... t) { diff --git a/expression_fwd.hpp b/expression_fwd.hpp index ca727e1..26091d0 100644 --- a/expression_fwd.hpp +++ b/expression_fwd.hpp @@ -60,7 +60,7 @@ namespace boost::proto17 { subscript, // [] // ternary - // TODO: if_else, // (analogous to) ?: + if_else, // (analogous to) ?: // n-ary call // () @@ -161,6 +161,9 @@ namespace boost::proto17 { struct bitwise_xor_assign_tag {}; // ^= struct subscript_tag {}; // [] + // ternary + struct if_else_tag {}; // (analogous to) ?: + // n-ary struct call_tag {}; // () diff --git a/operators.hpp b/operators.hpp index 3a4afc2..acd0aa3 100644 --- a/operators.hpp +++ b/operators.hpp @@ -128,6 +128,18 @@ namespace boost::proto17 { #undef BOOST_PROTO17_BINARY_OPERATOR + template + constexpr auto eval_if_else (T && t, U && u, V && v) BOOST_PROTO17_NOEXCEPT_DECLTYPE_RETURN( + static_cast(t) ? static_cast(u) : static_cast(v) + ) + struct eval_if_else_fn + { + template + constexpr auto operator() (T && t, U && u, V && v) const BOOST_PROTO17_NOEXCEPT_DECLTYPE_RETURN( + eval_if_else(static_cast(t), static_cast(u), static_cast(v)) + ) + }; + template constexpr auto eval_call (F && f, T && ...t) BOOST_PROTO17_NOEXCEPT_DECLTYPE_RETURN( static_cast(f)(static_cast(t)...) @@ -188,6 +200,7 @@ namespace boost::proto17 { BOOST_PROTO17_USING_OPERATOR_FN(bitwise_or_assign) // |= BOOST_PROTO17_USING_OPERATOR_FN(bitwise_xor_assign) // ^= BOOST_PROTO17_USING_OPERATOR_FN(subscript) // [] + BOOST_PROTO17_USING_OPERATOR_FN(if_else) // (analogous to) ?: BOOST_PROTO17_USING_OPERATOR_FN(call) // () #undef BOOST_PROTO17_USING_OPERATOR_FN @@ -239,6 +252,7 @@ namespace boost::proto17 { BOOST_PROTO17_DECLARE_OPERATOR_FN(bitwise_or_assign) // |= BOOST_PROTO17_DECLARE_OPERATOR_FN(bitwise_xor_assign) // ^= BOOST_PROTO17_DECLARE_OPERATOR_FN(subscript) // [] + BOOST_PROTO17_DECLARE_OPERATOR_FN(if_else) // (analogous to) ?: BOOST_PROTO17_DECLARE_OPERATOR_FN(call) // () #undef BOOST_PROTO17_DECLARE_OPERATOR_FN diff --git a/user_macros.hpp b/user_macros.hpp index 8d86137..88dd172 100644 --- a/user_macros.hpp +++ b/user_macros.hpp @@ -214,6 +214,68 @@ } \ +#define BOOST_PROTO17_USER_EXPR_IF_ELSE(expr_template) \ + template \ + auto if_else (Expr1 && expr1, Expr2 && expr2, Expr3 && expr3) \ + -> ::boost::proto17::detail::ternary_op_result_t< \ + expr_template, \ + Expr1, \ + Expr2, \ + Expr3 \ + > \ + { \ + using result_types = ::boost::proto17::detail::ternary_op_result< \ + expr_template, \ + Expr1, \ + Expr2, \ + Expr3 \ + >; \ + using cond_type = typename result_types::cond_type; \ + using then_type = typename result_types::then_type; \ + using else_type = typename result_types::else_type; \ + using tuple_type = ::boost::hana::tuple; \ + return { \ + tuple_type{ \ + ::boost::proto17::detail::make_operand{}(static_cast(expr1)), \ + ::boost::proto17::detail::make_operand{}(static_cast(expr2)), \ + ::boost::proto17::detail::make_operand{}(static_cast(expr3)) \ + } \ + }; \ + } + + +#define BOOST_PROTO17_USER_UDT_ANY_IF_ELSE(expr_template, udt_trait) \ + template \ + auto if_else (Expr1 && expr1, Expr2 && expr2, Expr3 && expr3) \ + -> ::boost::proto17::detail::udt_any_ternary_op_result_t< \ + expr_template, \ + Expr1, \ + Expr2, \ + Expr3, \ + udt_trait \ + > \ + { \ + using result_types = ::boost::proto17::detail::udt_any_ternary_op_result< \ + expr_template, \ + Expr1, \ + Expr2, \ + Expr3, \ + udt_trait \ + >; \ + using cond_type = typename result_types::cond_type; \ + using then_type = typename result_types::then_type; \ + using else_type = typename result_types::else_type; \ + using tuple_type = ::boost::hana::tuple; \ + return { \ + tuple_type{ \ + ::boost::proto17::detail::make_operand{}(static_cast(expr1)), \ + ::boost::proto17::detail::make_operand{}(static_cast(expr2)), \ + ::boost::proto17::detail::make_operand{}(static_cast(expr3)) \ + } \ + }; \ + } + + #define BOOST_PROTO17_USER_UDT_UNARY_OPERATOR(op_name, expr_template, udt_trait) \ template \ auto operator BOOST_PROTO17_INDIRECT_CALL(op_name)((T && x)) \