/*! @file Defines `boost::hana::infix`. @copyright Louis Dionne 2013-2017 Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) */ #ifndef BOOST_HANA_FUNCTIONAL_INFIX_HPP #define BOOST_HANA_FUNCTIONAL_INFIX_HPP #include #include #include #include #include #include namespace boost { namespace hana { //! @ingroup group-functional //! Return an equivalent function that can also be applied in infix //! notation. //! //! Specifically, `infix(f)` is an object such that: //! @code //! infix(f)(x1, ..., xn) == f(x1, ..., xn) //! x ^infix(f)^ y == f(x, y) //! @endcode //! //! Hence, the returned function can still be applied using the usual //! function call syntax, but it also gains the ability to be applied in //! infix notation. The infix syntax allows a great deal of expressiveness, //! especially when used in combination with some higher order algorithms. //! Since `operator^` is left-associative, `x ^infix(f)^ y` is actually //! parsed as `(x ^infix(f))^ y`. However, for flexibility, the order in //! which both arguments are applied in infix notation does not matter. //! Hence, it is always the case that //! @code //! (x ^ infix(f)) ^ y == x ^ (infix(f) ^ y) //! @endcode //! //! However, note that applying more than one argument in infix //! notation to the same side of the operator will result in a //! compile-time assertion: //! @code //! (infix(f) ^ x) ^ y; // compile-time assertion //! y ^ (x ^ infix(f)); // compile-time assertion //! @endcode //! //! Additionally, a function created with `infix` may be partially applied //! in infix notation. Specifically, //! @code //! (x ^ infix(f))(y1, ..., yn) == f(x, y1, ..., yn) //! (infix(f) ^ y)(x1, ..., xn) == f(x1, ..., xn, y) //! @endcode //! //! @internal //! ### Rationales //! 1. The `^` operator was chosen because it is left-associative and //! has a low enough priority so that most expressions will render //! the expected behavior. //! 2. The operator can't be customimzed because that would require more //! sophistication in the implementation; I want to keep it as simple //! as possible. There is also an advantage in having a uniform syntax //! for infix application. //! @endinternal //! //! @param f //! The function which gains the ability to be applied in infix notation. //! The function must be at least binary; a compile-time error will be //! triggered otherwise. //! //! ### Example //! @include example/functional/infix.cpp #ifdef BOOST_HANA_DOXYGEN_INVOKED constexpr auto infix = [](auto f) { return unspecified; }; #else namespace infix_detail { // This needs to be in the same namespace as `operator^` so it can be // found by ADL. template struct infix_t { F f; template constexpr decltype(auto) operator()(X&& ...x) const& { return f(static_cast(x)...); } template constexpr decltype(auto) operator()(X&& ...x) & { return f(static_cast(x)...); } template constexpr decltype(auto) operator()(X&& ...x) && { return std::move(f)(static_cast(x)...); } }; template struct make_infix { template constexpr infix_t::type> operator()(F&& f) const { return {static_cast(f)}; } }; template struct Infix; struct Object; template struct dispatch { using type = Object; }; template struct dispatch> { using type = Infix; }; template struct bind_infix; // infix(f) ^ y template <> struct bind_infix, Object> { template static constexpr decltype(auto) apply(F&& f, Y&& y) { return make_infix{}( hana::reverse_partial( static_cast(f), static_cast(y) ) ); } }; // (x^infix(f)) ^ y template <> struct bind_infix, Object> { template static constexpr decltype(auto) apply(F&& f, Y&& y) { return static_cast(f)(static_cast(y)); } }; // x ^ infix(f) template <> struct bind_infix> { template static constexpr decltype(auto) apply(X&& x, F&& f) { return make_infix{}( hana::partial(static_cast(f), static_cast(x)) ); } }; // x ^ (infix(f)^y) template <> struct bind_infix> { template static constexpr decltype(auto) apply(X&& x, F&& f) { return static_cast(f)(static_cast(x)); } }; template using strip = typename std::remove_cv< typename std::remove_reference::type >::type; template constexpr decltype(auto) operator^(X&& x, Y&& y) { return bind_infix< typename dispatch>::type, typename dispatch>::type >::apply(static_cast(x), static_cast(y)); } } // end namespace infix_detail BOOST_HANA_INLINE_VARIABLE constexpr infix_detail::make_infix infix{}; #endif }} // end namespace boost::hana #endif // !BOOST_HANA_FUNCTIONAL_INFIX_HPP