3 Defines `boost::hana::infix`.
5 @copyright Louis Dionne 2013-2017
6 Distributed under the Boost Software License, Version 1.0.
7 (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
10 #ifndef BOOST_HANA_FUNCTIONAL_INFIX_HPP
11 #define BOOST_HANA_FUNCTIONAL_INFIX_HPP
13 #include <boost/hana/config.hpp>
14 #include <boost/hana/detail/decay.hpp>
15 #include <boost/hana/functional/partial.hpp>
16 #include <boost/hana/functional/reverse_partial.hpp>
18 #include <type_traits>
22 BOOST_HANA_NAMESPACE_BEGIN
23 //! @ingroup group-functional
24 //! Return an equivalent function that can also be applied in infix
27 //! Specifically, `infix(f)` is an object such that:
29 //! infix(f)(x1, ..., xn) == f(x1, ..., xn)
30 //! x ^infix(f)^ y == f(x, y)
33 //! Hence, the returned function can still be applied using the usual
34 //! function call syntax, but it also gains the ability to be applied in
35 //! infix notation. The infix syntax allows a great deal of expressiveness,
36 //! especially when used in combination with some higher order algorithms.
37 //! Since `operator^` is left-associative, `x ^infix(f)^ y` is actually
38 //! parsed as `(x ^infix(f))^ y`. However, for flexibility, the order in
39 //! which both arguments are applied in infix notation does not matter.
40 //! Hence, it is always the case that
42 //! (x ^ infix(f)) ^ y == x ^ (infix(f) ^ y)
45 //! However, note that applying more than one argument in infix
46 //! notation to the same side of the operator will result in a
47 //! compile-time assertion:
49 //! (infix(f) ^ x) ^ y; // compile-time assertion
50 //! y ^ (x ^ infix(f)); // compile-time assertion
53 //! Additionally, a function created with `infix` may be partially applied
54 //! in infix notation. Specifically,
56 //! (x ^ infix(f))(y1, ..., yn) == f(x, y1, ..., yn)
57 //! (infix(f) ^ y)(x1, ..., xn) == f(x1, ..., xn, y)
62 //! 1. The `^` operator was chosen because it is left-associative and
63 //! has a low enough priority so that most expressions will render
64 //! the expected behavior.
65 //! 2. The operator can't be customimzed because that would require more
66 //! sophistication in the implementation; I want to keep it as simple
67 //! as possible. There is also an advantage in having a uniform syntax
68 //! for infix application.
72 //! The function which gains the ability to be applied in infix notation.
73 //! The function must be at least binary; a compile-time error will be
74 //! triggered otherwise.
77 //! @include example/functional/infix.cpp
78 #ifdef BOOST_HANA_DOXYGEN_INVOKED
79 constexpr auto infix = [](auto f) {
83 namespace infix_detail {
84 // This needs to be in the same namespace as `operator^` so it can be
86 template <bool left, bool right, typename F>
90 template <typename ...X>
91 constexpr decltype(auto) operator()(X&& ...x) const&
92 { return f(static_cast<X&&>(x)...); }
94 template <typename ...X>
95 constexpr decltype(auto) operator()(X&& ...x) &
96 { return f(static_cast<X&&>(x)...); }
98 template <typename ...X>
99 constexpr decltype(auto) operator()(X&& ...x) &&
100 { return std::move(f)(static_cast<X&&>(x)...); }
103 template <bool left, bool right>
105 template <typename F>
106 constexpr infix_t<left, right, typename detail::decay<F>::type>
107 operator()(F&& f) const { return {static_cast<F&&>(f)}; }
110 template <bool left, bool right>
114 template <typename T>
115 struct dispatch { using type = Object; };
117 template <bool left, bool right, typename F>
118 struct dispatch<infix_t<left, right, F>> {
119 using type = Infix<left, right>;
122 template <typename, typename>
127 struct bind_infix<Infix<false, false>, Object> {
128 template <typename F, typename Y>
129 static constexpr decltype(auto) apply(F&& f, Y&& y) {
130 return make_infix<false, true>{}(
131 hana::reverse_partial(
132 static_cast<F&&>(f), static_cast<Y&&>(y)
140 struct bind_infix<Infix<true, false>, Object> {
141 template <typename F, typename Y>
142 static constexpr decltype(auto) apply(F&& f, Y&& y) {
143 return static_cast<F&&>(f)(static_cast<Y&&>(y));
149 struct bind_infix<Object, Infix<false, false>> {
150 template <typename X, typename F>
151 static constexpr decltype(auto) apply(X&& x, F&& f) {
152 return make_infix<true, false>{}(
153 hana::partial(static_cast<F&&>(f), static_cast<X&&>(x))
160 struct bind_infix<Object, Infix<false, true>> {
161 template <typename X, typename F>
162 static constexpr decltype(auto) apply(X&& x, F&& f) {
163 return static_cast<F&&>(f)(static_cast<X&&>(x));
167 template <typename T>
168 using strip = typename std::remove_cv<
169 typename std::remove_reference<T>::type
172 template <typename X, typename Y>
173 constexpr decltype(auto) operator^(X&& x, Y&& y) {
175 typename dispatch<strip<X>>::type,
176 typename dispatch<strip<Y>>::type
177 >::apply(static_cast<X&&>(x), static_cast<Y&&>(y));
179 } // end namespace infix_detail
181 constexpr infix_detail::make_infix<false, false> infix{};
183 BOOST_HANA_NAMESPACE_END
185 #endif // !BOOST_HANA_FUNCTIONAL_INFIX_HPP