infix.hpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. /*!
  2. @file
  3. Defines `boost::hana::infix`.
  4. @copyright Louis Dionne 2013-2017
  5. Distributed under the Boost Software License, Version 1.0.
  6. (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
  7. */
  8. #ifndef BOOST_HANA_FUNCTIONAL_INFIX_HPP
  9. #define BOOST_HANA_FUNCTIONAL_INFIX_HPP
  10. #include <boost/hana/config.hpp>
  11. #include <boost/hana/detail/decay.hpp>
  12. #include <boost/hana/functional/partial.hpp>
  13. #include <boost/hana/functional/reverse_partial.hpp>
  14. #include <type_traits>
  15. #include <utility>
  16. BOOST_HANA_NAMESPACE_BEGIN
  17. //! @ingroup group-functional
  18. //! Return an equivalent function that can also be applied in infix
  19. //! notation.
  20. //!
  21. //! Specifically, `infix(f)` is an object such that:
  22. //! @code
  23. //! infix(f)(x1, ..., xn) == f(x1, ..., xn)
  24. //! x ^infix(f)^ y == f(x, y)
  25. //! @endcode
  26. //!
  27. //! Hence, the returned function can still be applied using the usual
  28. //! function call syntax, but it also gains the ability to be applied in
  29. //! infix notation. The infix syntax allows a great deal of expressiveness,
  30. //! especially when used in combination with some higher order algorithms.
  31. //! Since `operator^` is left-associative, `x ^infix(f)^ y` is actually
  32. //! parsed as `(x ^infix(f))^ y`. However, for flexibility, the order in
  33. //! which both arguments are applied in infix notation does not matter.
  34. //! Hence, it is always the case that
  35. //! @code
  36. //! (x ^ infix(f)) ^ y == x ^ (infix(f) ^ y)
  37. //! @endcode
  38. //!
  39. //! However, note that applying more than one argument in infix
  40. //! notation to the same side of the operator will result in a
  41. //! compile-time assertion:
  42. //! @code
  43. //! (infix(f) ^ x) ^ y; // compile-time assertion
  44. //! y ^ (x ^ infix(f)); // compile-time assertion
  45. //! @endcode
  46. //!
  47. //! Additionally, a function created with `infix` may be partially applied
  48. //! in infix notation. Specifically,
  49. //! @code
  50. //! (x ^ infix(f))(y1, ..., yn) == f(x, y1, ..., yn)
  51. //! (infix(f) ^ y)(x1, ..., xn) == f(x1, ..., xn, y)
  52. //! @endcode
  53. //!
  54. //! @internal
  55. //! ### Rationales
  56. //! 1. The `^` operator was chosen because it is left-associative and
  57. //! has a low enough priority so that most expressions will render
  58. //! the expected behavior.
  59. //! 2. The operator can't be customimzed because that would require more
  60. //! sophistication in the implementation; I want to keep it as simple
  61. //! as possible. There is also an advantage in having a uniform syntax
  62. //! for infix application.
  63. //! @endinternal
  64. //!
  65. //! @param f
  66. //! The function which gains the ability to be applied in infix notation.
  67. //! The function must be at least binary; a compile-time error will be
  68. //! triggered otherwise.
  69. //!
  70. //! ### Example
  71. //! @include example/functional/infix.cpp
  72. #ifdef BOOST_HANA_DOXYGEN_INVOKED
  73. constexpr auto infix = [](auto f) {
  74. return unspecified;
  75. };
  76. #else
  77. namespace infix_detail {
  78. // This needs to be in the same namespace as `operator^` so it can be
  79. // found by ADL.
  80. template <bool left, bool right, typename F>
  81. struct infix_t {
  82. F f;
  83. template <typename ...X>
  84. constexpr decltype(auto) operator()(X&& ...x) const&
  85. { return f(static_cast<X&&>(x)...); }
  86. template <typename ...X>
  87. constexpr decltype(auto) operator()(X&& ...x) &
  88. { return f(static_cast<X&&>(x)...); }
  89. template <typename ...X>
  90. constexpr decltype(auto) operator()(X&& ...x) &&
  91. { return std::move(f)(static_cast<X&&>(x)...); }
  92. };
  93. template <bool left, bool right>
  94. struct make_infix {
  95. template <typename F>
  96. constexpr infix_t<left, right, typename detail::decay<F>::type>
  97. operator()(F&& f) const { return {static_cast<F&&>(f)}; }
  98. };
  99. template <bool left, bool right>
  100. struct Infix;
  101. struct Object;
  102. template <typename T>
  103. struct dispatch { using type = Object; };
  104. template <bool left, bool right, typename F>
  105. struct dispatch<infix_t<left, right, F>> {
  106. using type = Infix<left, right>;
  107. };
  108. template <typename, typename>
  109. struct bind_infix;
  110. // infix(f) ^ y
  111. template <>
  112. struct bind_infix<Infix<false, false>, Object> {
  113. template <typename F, typename Y>
  114. static constexpr decltype(auto) apply(F&& f, Y&& y) {
  115. return make_infix<false, true>{}(
  116. hana::reverse_partial(
  117. static_cast<F&&>(f), static_cast<Y&&>(y)
  118. )
  119. );
  120. }
  121. };
  122. // (x^infix(f)) ^ y
  123. template <>
  124. struct bind_infix<Infix<true, false>, Object> {
  125. template <typename F, typename Y>
  126. static constexpr decltype(auto) apply(F&& f, Y&& y) {
  127. return static_cast<F&&>(f)(static_cast<Y&&>(y));
  128. }
  129. };
  130. // x ^ infix(f)
  131. template <>
  132. struct bind_infix<Object, Infix<false, false>> {
  133. template <typename X, typename F>
  134. static constexpr decltype(auto) apply(X&& x, F&& f) {
  135. return make_infix<true, false>{}(
  136. hana::partial(static_cast<F&&>(f), static_cast<X&&>(x))
  137. );
  138. }
  139. };
  140. // x ^ (infix(f)^y)
  141. template <>
  142. struct bind_infix<Object, Infix<false, true>> {
  143. template <typename X, typename F>
  144. static constexpr decltype(auto) apply(X&& x, F&& f) {
  145. return static_cast<F&&>(f)(static_cast<X&&>(x));
  146. }
  147. };
  148. template <typename T>
  149. using strip = typename std::remove_cv<
  150. typename std::remove_reference<T>::type
  151. >::type;
  152. template <typename X, typename Y>
  153. constexpr decltype(auto) operator^(X&& x, Y&& y) {
  154. return bind_infix<
  155. typename dispatch<strip<X>>::type,
  156. typename dispatch<strip<Y>>::type
  157. >::apply(static_cast<X&&>(x), static_cast<Y&&>(y));
  158. }
  159. } // end namespace infix_detail
  160. constexpr infix_detail::make_infix<false, false> infix{};
  161. #endif
  162. BOOST_HANA_NAMESPACE_END
  163. #endif // !BOOST_HANA_FUNCTIONAL_INFIX_HPP