/* @copyright Louis Dionne 2014 Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) */ #include #include #include #include #include namespace hana = boost::hana; // Difficulties: // 1. eval_if(false_, ...) will use Logical because it will try // to eval(false_), which is false_(), which is false. // // possible workaround 1: do not try false_() in eval(false_), and // whenever you want to have a lambda branch you _must_ write // [](auto) { ... } instead of []{ ... } // // possible workaround 2: first try to dispatch to the condition // and then try to dispatch to eval(cond) in Logical. This is the // current solution. // // 2. could we explicitly say "i want this branch evaluated"? // something like if_(eval(cond), eval(branch1), eval(branch2)). // // // Warning: make sure the whole approach with `eval` is not // fundamentally broken. There seems to be some loss of information // in eval(), which I suspect breaks the whole thing in subtle cases. struct Logical : hana::typeclass { struct mcd; struct default_; }; BOOST_HANA_CONSTEXPR_LAMBDA auto if_ = [](auto logical, auto then_, auto else_) { return Logical::instance>::if_impl(logical, then_, else_); }; BOOST_HANA_CONSTEXPR_LAMBDA auto eval_if = [](auto logical, auto then_, auto else_) { return Logical::instance>::eval_if_impl(logical, then_, else_); }; BOOST_HANA_CONSTEXPR_LAMBDA auto eval_else_if = eval_if; BOOST_HANA_CONSTEXPR_LAMBDA auto eval_else = hana::id; BOOST_HANA_CONSTEXPR_LAMBDA auto else_if = if_; BOOST_HANA_CONSTEXPR_LAMBDA auto else_ = hana::id; constexpr struct _eval { template static constexpr auto call(F f, int) -> decltype(f(hana::id)) { return f(hana::id); } template static constexpr auto call(F f, long) -> decltype(f()) { return f(); } template static constexpr auto call(F f, ...) { return f; } template constexpr auto operator()(F f) const { return call(f, int{0}); } template constexpr auto operator[](F f) const { return [=](auto ...args) { return (*this)(f(args...)); }; } } eval{}; struct Logical::mcd { template static constexpr auto if_impl(Cond c, Then t, Else e) { return eval_if(c, hana::always(t), hana::always(e)); } }; struct Logical::default_ : Logical::mcd { template static constexpr auto eval_if_impl(Cond c, Then t, Else e) { auto would_recurse = hana::decltype_(eval(c)) == hana::decltype_(c); static_assert(!would_recurse(), "Condition is probably not a logical. Trying to evaluate " "it and use it as the condition to if_ again would cause " "infinite recursion."); return eval_if(eval(c), t, e); } }; template <> struct Logical::instance : Logical::mcd { template static constexpr auto eval_if_impl(bool cond, Then t, Else e) { return cond ? eval(t) : eval(e); } }; template <> struct Logical::instance : Logical::mcd { template static constexpr auto eval_if_impl(Cond c, Then t, Else e) { return eval_if_impl(hana::bool_, t, e); } template static constexpr auto eval_if_impl(decltype(hana::true_), Then t, Else) { return eval(t); } template static constexpr auto eval_if_impl(decltype(hana::false_), Then, Else e) { return eval(e); } }; ////////////////////////////////////////////////////////////////////////////// #include #include using hana::true_; using hana::false_; using hana::type; struct T; struct E; void test_eval() { BOOST_HANA_STATIC_ASSERT(eval([] { return 1; }) == 1); BOOST_HANA_STATIC_ASSERT(eval([](auto id) { return id(1); }) == 1); BOOST_HANA_STATIC_ASSERT(eval(1) == 1); } void test_bool_logical() { BOOST_HANA_STATIC_ASSERT(if_(true, true, false) == true); BOOST_HANA_STATIC_ASSERT(if_(false, true, false) == false); } void test_integral_logical() { // can also support heterogeneous branches BOOST_HANA_STATIC_ASSERT(if_(true_, type, type) == type); BOOST_HANA_STATIC_ASSERT(if_(false_, type, type) == type); } void test_eval_condition() { BOOST_HANA_STATIC_ASSERT(if_([]{ return true; }, true, false) == true); BOOST_HANA_STATIC_ASSERT(if_([]{ return false; }, true, false) == false); BOOST_HANA_STATIC_ASSERT(if_([]{ return true_; }, type, type) == type); BOOST_HANA_STATIC_ASSERT(if_([]{ return false_; }, type, type) == type); BOOST_HANA_STATIC_ASSERT(if_([](auto) { return true; }, true, false) == true); BOOST_HANA_STATIC_ASSERT(if_([](auto) { return false; }, true, false) == false); BOOST_HANA_STATIC_ASSERT(if_([](auto) { return true_; }, type, type) == type); BOOST_HANA_STATIC_ASSERT(if_([](auto) { return false_; }, type, type) == type); } void test_eval_branches() { BOOST_HANA_STATIC_ASSERT(eval_if(true, []{ return true; }, []{ return false; }) == true); BOOST_HANA_STATIC_ASSERT(eval_if(false, []{ return true; }, []{ return false; }) == false); BOOST_HANA_STATIC_ASSERT(eval_if(true_, []{ return type; }, []{ return type; }) == type); BOOST_HANA_STATIC_ASSERT(eval_if(false_, []{ return type; }, []{ return type; }) == type); BOOST_HANA_STATIC_ASSERT(eval_if(true, [](auto) { return true; }, [](auto) { return false; }) == true); BOOST_HANA_STATIC_ASSERT(eval_if(false, [](auto) { return true; }, [](auto) { return false; }) == false); BOOST_HANA_STATIC_ASSERT(eval_if(true_, [](auto) { return type; }, [](auto) { return type; }) == type); BOOST_HANA_STATIC_ASSERT(eval_if(false_, [](auto) { return type; }, [](auto) { return type; }) == type); } template BOOST_HANA_CONSTEXPR_LAMBDA auto branch = hana::decltype_(hana::int_); void test_multi_branch() { BOOST_HANA_STATIC_ASSERT( eval_if(false_, []{ return branch<1>; }, // How to remove the need for a lambda? [] { return eval_if([] { return true_; }, [](auto) { return branch<2>; }, else_( [](auto) { return branch<3>; } )); } ) == branch<2> ); BOOST_HANA_STATIC_ASSERT( eval_if([](auto) { return false_; }, [](auto) { return branch<1>; }, eval_if(true, 2, else_( 3 ))) == 2 ); } void test_syntactic_sugar() { #if 0 if_(cond)([]{ }) +else_if(cond)([]{ }) +else_(if_(cond)( )) +else_( ) auto else_if = curry<3>([](auto c, auto t, auto e) { return else_(if_(c, t, e)); }); #endif } int main() { test_eval(); test_bool_logical(); test_integral_logical(); test_eval_condition(); test_eval_branches(); test_multi_branch(); test_syntactic_sugar(); }