infix.hpp 6.3 KB

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