| 1 | /*! |
| 2 | @file |
| 3 | Defines `boost::hana::_`. |
| 4 | |
| 5 | Copyright Louis Dionne 2013-2022 |
| 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) |
| 8 | */ |
| 9 | |
| 10 | #ifndef BOOST_HANA_FUNCTIONAL_PLACEHOLDER_HPP |
| 11 | #define BOOST_HANA_FUNCTIONAL_PLACEHOLDER_HPP |
| 12 | |
| 13 | #include <boost/hana/basic_tuple.hpp> |
| 14 | #include <boost/hana/config.hpp> |
| 15 | #include <boost/hana/detail/create.hpp> |
| 16 | #include <boost/hana/detail/decay.hpp> |
| 17 | |
| 18 | #include <cstddef> |
| 19 | #include <utility> |
| 20 | |
| 21 | |
| 22 | namespace boost { namespace hana { |
| 23 | //! @ingroup group-functional |
| 24 | //! Create simple functions representing C++ operators inline. |
| 25 | //! |
| 26 | //! Specifically, `_` is an object used as a placeholder to build |
| 27 | //! function objects representing calls to C++ operators. It works |
| 28 | //! by overloading the operators between `_` and any object so that |
| 29 | //! they return a function object which actually calls the corresponding |
| 30 | //! operator on its argument(s). Hence, for any supported operator `@`: |
| 31 | //! @code |
| 32 | //! (_ @ _)(x, y) == x @ y |
| 33 | //! @endcode |
| 34 | //! |
| 35 | //! Operators may also be partially applied to one argument inline: |
| 36 | //! @code |
| 37 | //! (x @ _)(y) == x @ y |
| 38 | //! (_ @ y)(x) == x @ y |
| 39 | //! @endcode |
| 40 | //! |
| 41 | //! When invoked with more arguments than required, functions created with |
| 42 | //! `_` will discard the superfluous instead of triggering an error: |
| 43 | //! @code |
| 44 | //! (_ @ _)(x, y, z...) == x @ y |
| 45 | //! @endcode |
| 46 | //! |
| 47 | //! This makes functions created with `_` easier to use in higher-order |
| 48 | //! algorithms, which sometime provide more information than necessary |
| 49 | //! to their callbacks. |
| 50 | //! |
| 51 | //! ### Supported operators |
| 52 | //! - Arithmetic: binary `+`, binary `-`, `/`, `*`, `%`, unary `+`, unary `-` |
| 53 | //! - Bitwise: `~`, `&`, `|`, `^`, `<<`, `>>` |
| 54 | //! - Comparison: `==`, `!=`, `<`, `<=`, `>`, `>=` |
| 55 | //! - %Logical: `||`, `&&`, `!` |
| 56 | //! - Member access: `*` (dereference), `[]` (array subscript) |
| 57 | //! - Other: `()` (function call) |
| 58 | //! |
| 59 | //! More complex functionality like the ability to compose placeholders |
| 60 | //! into larger function objects inline are not supported. This is on |
| 61 | //! purpose; you should either use C++14 generic lambdas or a library |
| 62 | //! like [Boost.Phoenix][] if you need bigger guns. The goal here is |
| 63 | //! to save you a couple of characters in simple situations. |
| 64 | //! |
| 65 | //! ### Example |
| 66 | //! @include example/functional/placeholder.cpp |
| 67 | //! |
| 68 | //! [Boost.Phoenix]: http://www.boost.org/doc/libs/release/libs/phoenix/doc/html/index.html |
| 69 | #ifdef BOOST_HANA_DOXYGEN_INVOKED |
| 70 | constexpr unspecified _{}; |
| 71 | #else |
| 72 | namespace placeholder_detail { |
| 73 | template <typename I> |
| 74 | struct subscript { |
| 75 | I i; |
| 76 | |
| 77 | template <typename Xs, typename ...Z> |
| 78 | constexpr auto operator()(Xs&& xs, Z const& ...) const& |
| 79 | -> decltype(static_cast<Xs&&>(xs)[i]) |
| 80 | { return static_cast<Xs&&>(xs)[i]; } |
| 81 | |
| 82 | template <typename Xs, typename ...Z> |
| 83 | constexpr auto operator()(Xs&& xs, Z const& ...) & |
| 84 | -> decltype(static_cast<Xs&&>(xs)[i]) |
| 85 | { return static_cast<Xs&&>(xs)[i]; } |
| 86 | |
| 87 | template <typename Xs, typename ...Z> |
| 88 | constexpr auto operator()(Xs&& xs, Z const& ...) && |
| 89 | -> decltype(static_cast<Xs&&>(xs)[std::declval<I>()]) |
| 90 | { return static_cast<Xs&&>(xs)[std::move(i)]; } |
| 91 | }; |
| 92 | |
| 93 | template <typename F, typename Xs, std::size_t ...i> |
| 94 | constexpr decltype(auto) invoke_impl(F&& f, Xs&& xs, std::index_sequence<i...>) { |
| 95 | return static_cast<F&&>(f)(hana::at_c<i>(static_cast<Xs&&>(xs).storage_)...); |
| 96 | } |
| 97 | |
| 98 | template <typename ...X> |
| 99 | struct invoke; |
| 100 | |
| 101 | struct placeholder { |
| 102 | struct secret { }; |
| 103 | |
| 104 | template <typename X> |
| 105 | constexpr decltype(auto) operator[](X&& x) const |
| 106 | { return detail::create<subscript>{}(static_cast<X&&>(x)); } |
| 107 | |
| 108 | template <typename ...X> |
| 109 | constexpr invoke<typename detail::decay<X>::type...> |
| 110 | operator()(X&& ...x) const { |
| 111 | return {secret{}, static_cast<X&&>(x)...}; |
| 112 | } |
| 113 | }; |
| 114 | |
| 115 | template <typename ...X> |
| 116 | struct invoke { |
| 117 | template <typename ...Y> |
| 118 | constexpr invoke(placeholder::secret, Y&& ...y) |
| 119 | : storage_{static_cast<Y&&>(y)...} |
| 120 | { } |
| 121 | |
| 122 | basic_tuple<X...> storage_; |
| 123 | |
| 124 | template <typename F, typename ...Z> |
| 125 | constexpr auto operator()(F&& f, Z const& ...) const& -> decltype( |
| 126 | static_cast<F&&>(f)(std::declval<X const&>()...) |
| 127 | ) { |
| 128 | return invoke_impl(static_cast<F&&>(f), *this, |
| 129 | std::make_index_sequence<sizeof...(X)>{}); |
| 130 | } |
| 131 | |
| 132 | template <typename F, typename ...Z> |
| 133 | constexpr auto operator()(F&& f, Z const& ...) & -> decltype( |
| 134 | static_cast<F&&>(f)(std::declval<X&>()...) |
| 135 | ) { |
| 136 | return invoke_impl(static_cast<F&&>(f), *this, |
| 137 | std::make_index_sequence<sizeof...(X)>{}); |
| 138 | } |
| 139 | |
| 140 | template <typename F, typename ...Z> |
| 141 | constexpr auto operator()(F&& f, Z const& ...) && -> decltype( |
| 142 | static_cast<F&&>(f)(std::declval<X&&>()...) |
| 143 | ) { |
| 144 | return invoke_impl(static_cast<F&&>(f), static_cast<invoke&&>(*this), |
| 145 | std::make_index_sequence<sizeof...(X)>{}); |
| 146 | } |
| 147 | }; |
| 148 | |
| 149 | #define BOOST_HANA_PLACEHOLDER_BINARY_OP(op, op_name) \ |
| 150 | template <typename X> \ |
| 151 | struct op_name ## _left { \ |
| 152 | X x; \ |
| 153 | \ |
| 154 | template <typename Y, typename ...Z> \ |
| 155 | constexpr auto operator()(Y&& y, Z const& ...) const& -> decltype( \ |
| 156 | std::declval<X const&>() op static_cast<Y&&>(y)) \ |
| 157 | { return x op static_cast<Y&&>(y); } \ |
| 158 | \ |
| 159 | template <typename Y, typename ...Z> \ |
| 160 | constexpr auto operator()(Y&& y, Z const& ...) & -> decltype( \ |
| 161 | std::declval<X&>() op static_cast<Y&&>(y)) \ |
| 162 | { return x op static_cast<Y&&>(y); } \ |
| 163 | \ |
| 164 | template <typename Y, typename ...Z> \ |
| 165 | constexpr auto operator()(Y&& y, Z const& ...) && -> decltype( \ |
| 166 | std::declval<X>() op static_cast<Y&&>(y)) \ |
| 167 | { return std::move(x) op static_cast<Y&&>(y); } \ |
| 168 | }; \ |
| 169 | \ |
| 170 | template <typename Y> \ |
| 171 | struct op_name ## _right { \ |
| 172 | Y y; \ |
| 173 | \ |
| 174 | template <typename X, typename ...Z> \ |
| 175 | constexpr auto operator()(X&& x, Z const& ...) const& -> decltype( \ |
| 176 | static_cast<X&&>(x) op std::declval<Y const&>()) \ |
| 177 | { return static_cast<X&&>(x) op y; } \ |
| 178 | \ |
| 179 | template <typename X, typename ...Z> \ |
| 180 | constexpr auto operator()(X&& x, Z const& ...) & -> decltype( \ |
| 181 | static_cast<X&&>(x) op std::declval<Y&>()) \ |
| 182 | { return static_cast<X&&>(x) op y; } \ |
| 183 | \ |
| 184 | template <typename X, typename ...Z> \ |
| 185 | constexpr auto operator()(X&& x, Z const& ...) && -> decltype( \ |
| 186 | static_cast<X&&>(x) op std::declval<Y>()) \ |
| 187 | { return static_cast<X&&>(x) op std::move(y); } \ |
| 188 | }; \ |
| 189 | \ |
| 190 | struct op_name { \ |
| 191 | template <typename X, typename Y, typename ...Z> \ |
| 192 | constexpr auto operator()(X&& x, Y&& y, Z const& ...) const -> decltype(\ |
| 193 | static_cast<X&&>(x) op static_cast<Y&&>(y)) \ |
| 194 | { return static_cast<X&&>(x) op static_cast<Y&&>(y); } \ |
| 195 | }; \ |
| 196 | \ |
| 197 | template <typename X> \ |
| 198 | constexpr decltype(auto) operator op (X&& x, placeholder) \ |
| 199 | { return detail::create<op_name ## _left>{}(static_cast<X&&>(x)); } \ |
| 200 | \ |
| 201 | template <typename Y> \ |
| 202 | constexpr decltype(auto) operator op (placeholder, Y&& y) \ |
| 203 | { return detail::create<op_name ## _right>{}(static_cast<Y&&>(y)); } \ |
| 204 | \ |
| 205 | inline constexpr decltype(auto) operator op (placeholder, placeholder) \ |
| 206 | { return op_name{}; } \ |
| 207 | /**/ |
| 208 | |
| 209 | #define BOOST_HANA_PLACEHOLDER_UNARY_OP(op, op_name) \ |
| 210 | struct op_name { \ |
| 211 | template <typename X, typename ...Z> \ |
| 212 | constexpr auto operator()(X&& x, Z const& ...) const \ |
| 213 | -> decltype(op static_cast<X&&>(x)) \ |
| 214 | { return op static_cast<X&&>(x); } \ |
| 215 | }; \ |
| 216 | \ |
| 217 | inline constexpr decltype(auto) operator op (placeholder) \ |
| 218 | { return op_name{}; } \ |
| 219 | /**/ |
| 220 | // Arithmetic |
| 221 | BOOST_HANA_PLACEHOLDER_UNARY_OP(+, unary_plus) |
| 222 | BOOST_HANA_PLACEHOLDER_UNARY_OP(-, unary_minus) |
| 223 | BOOST_HANA_PLACEHOLDER_BINARY_OP(+, plus) |
| 224 | BOOST_HANA_PLACEHOLDER_BINARY_OP(-, minus) |
| 225 | BOOST_HANA_PLACEHOLDER_BINARY_OP(*, times) |
| 226 | BOOST_HANA_PLACEHOLDER_BINARY_OP(/, divide) |
| 227 | BOOST_HANA_PLACEHOLDER_BINARY_OP(%, modulo) |
| 228 | |
| 229 | // Bitwise |
| 230 | BOOST_HANA_PLACEHOLDER_UNARY_OP(~, bitwise_not) |
| 231 | BOOST_HANA_PLACEHOLDER_BINARY_OP(&, bitwise_and) |
| 232 | BOOST_HANA_PLACEHOLDER_BINARY_OP(|, bitwise_or) |
| 233 | BOOST_HANA_PLACEHOLDER_BINARY_OP(^, bitwise_xor) |
| 234 | BOOST_HANA_PLACEHOLDER_BINARY_OP(<<, left_shift) |
| 235 | BOOST_HANA_PLACEHOLDER_BINARY_OP(>>, right_shift) |
| 236 | |
| 237 | // Comparison |
| 238 | BOOST_HANA_PLACEHOLDER_BINARY_OP(==, equal) |
| 239 | BOOST_HANA_PLACEHOLDER_BINARY_OP(!=, not_equal) |
| 240 | BOOST_HANA_PLACEHOLDER_BINARY_OP(<, less) |
| 241 | BOOST_HANA_PLACEHOLDER_BINARY_OP(<=, less_equal) |
| 242 | BOOST_HANA_PLACEHOLDER_BINARY_OP(>, greater) |
| 243 | BOOST_HANA_PLACEHOLDER_BINARY_OP(>=, greater_equal) |
| 244 | |
| 245 | // Logical |
| 246 | BOOST_HANA_PLACEHOLDER_BINARY_OP(||, logical_or) |
| 247 | BOOST_HANA_PLACEHOLDER_BINARY_OP(&&, logical_and) |
| 248 | BOOST_HANA_PLACEHOLDER_UNARY_OP(!, logical_not) |
| 249 | |
| 250 | // Member access (array subscript is a member function) |
| 251 | BOOST_HANA_PLACEHOLDER_UNARY_OP(*, dereference) |
| 252 | |
| 253 | // Other (function call is a member function) |
| 254 | |
| 255 | #undef BOOST_HANA_PREFIX_PLACEHOLDER_OP |
| 256 | #undef BOOST_HANA_BINARY_PLACEHOLDER_OP |
| 257 | } // end namespace placeholder_detail |
| 258 | |
| 259 | BOOST_HANA_INLINE_VARIABLE constexpr placeholder_detail::placeholder _{}; |
| 260 | #endif |
| 261 | }} // end namespace boost::hana |
| 262 | |
| 263 | #endif // !BOOST_HANA_FUNCTIONAL_PLACEHOLDER_HPP |
| 264 | |