| 1 | /*============================================================================= |
| 2 | Copyright (c) 2014 Paul Fultz II |
| 3 | pack.h |
| 4 | Distributed under the Boost Software License, Version 1.0. (See accompanying |
| 5 | file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
| 6 | ==============================================================================*/ |
| 7 | |
| 8 | #ifndef BOOST_HOF_GUARD_FUNCTION_PACK_H |
| 9 | #define BOOST_HOF_GUARD_FUNCTION_PACK_H |
| 10 | |
| 11 | /// pack |
| 12 | /// ==== |
| 13 | /// |
| 14 | /// Description |
| 15 | /// ----------- |
| 16 | /// |
| 17 | /// The `pack` function returns a higher order function object that takes a |
| 18 | /// function that will be passed the initial elements. The function object is |
| 19 | /// a sequence that can be unpacked with `unpack_adaptor` as well. Also, |
| 20 | /// `pack_join` can be used to join multiple packs together. |
| 21 | /// |
| 22 | /// Synopsis |
| 23 | /// -------- |
| 24 | /// |
| 25 | /// // Decay everything before capturing |
| 26 | /// template<class... Ts> |
| 27 | /// constexpr auto pack(Ts&&... xs); |
| 28 | /// |
| 29 | /// // Capture lvalues by reference and rvalue reference by reference |
| 30 | /// template<class... Ts> |
| 31 | /// constexpr auto pack_forward(Ts&&... xs); |
| 32 | /// |
| 33 | /// // Capture lvalues by reference and rvalues by value. |
| 34 | /// template<class... Ts> |
| 35 | /// constexpr auto pack_basic(Ts&&... xs); |
| 36 | /// |
| 37 | /// // Join multiple packs together |
| 38 | /// template<class... Ts> |
| 39 | /// constexpr auto pack_join(Ts&&... xs); |
| 40 | /// |
| 41 | /// Semantics |
| 42 | /// --------- |
| 43 | /// |
| 44 | /// assert(pack(xs...)(f) == f(xs...)); |
| 45 | /// assert(unpack(f)(pack(xs...)) == f(xs...)); |
| 46 | /// |
| 47 | /// assert(pack_join(pack(xs...), pack(ys...)) == pack(xs..., ys...)); |
| 48 | /// |
| 49 | /// |
| 50 | /// Example |
| 51 | /// ------- |
| 52 | /// |
| 53 | /// #include <boost/hof.hpp> |
| 54 | /// #include <cassert> |
| 55 | /// using namespace boost::hof; |
| 56 | /// |
| 57 | /// struct sum |
| 58 | /// { |
| 59 | /// template<class T, class U> |
| 60 | /// T operator()(T x, U y) const |
| 61 | /// { |
| 62 | /// return x+y; |
| 63 | /// } |
| 64 | /// }; |
| 65 | /// |
| 66 | /// int main() { |
| 67 | /// int r = pack(3, 2)(sum()); |
| 68 | /// assert(r == 5); |
| 69 | /// } |
| 70 | /// |
| 71 | /// See Also |
| 72 | /// -------- |
| 73 | /// |
| 74 | /// * [unpack](unpack) |
| 75 | /// |
| 76 | |
| 77 | #include <boost/hof/detail/seq.hpp> |
| 78 | #include <boost/hof/detail/delegate.hpp> |
| 79 | #include <boost/hof/detail/remove_rvalue_reference.hpp> |
| 80 | #include <boost/hof/detail/unwrap.hpp> |
| 81 | #include <boost/hof/detail/static_const_var.hpp> |
| 82 | #include <boost/hof/unpack_sequence.hpp> |
| 83 | #include <boost/hof/returns.hpp> |
| 84 | #include <boost/hof/alias.hpp> |
| 85 | #include <boost/hof/decay.hpp> |
| 86 | |
| 87 | namespace boost { namespace hof { namespace detail { |
| 88 | |
| 89 | template<class...> |
| 90 | struct pack_tag |
| 91 | {}; |
| 92 | |
| 93 | template<class T, class Tag> |
| 94 | struct pack_holder |
| 95 | : detail::alias_empty<T, Tag> |
| 96 | {}; |
| 97 | |
| 98 | template<class Seq, class... Ts> |
| 99 | struct pack_base; |
| 100 | |
| 101 | template<class T> |
| 102 | struct is_copyable |
| 103 | : std::integral_constant<bool, ( |
| 104 | BOOST_HOF_IS_CONSTRUCTIBLE(T, const T&) |
| 105 | )> |
| 106 | {}; |
| 107 | |
| 108 | template<class T> |
| 109 | struct is_copyable<T&> |
| 110 | : std::true_type |
| 111 | {}; |
| 112 | |
| 113 | template<class T> |
| 114 | struct is_copyable<T&&> |
| 115 | : std::true_type |
| 116 | {}; |
| 117 | |
| 118 | template<class T, class Tag, class X, class... Ts, typename std::enable_if< |
| 119 | is_copyable<T>::value && !std::is_lvalue_reference<T>::value |
| 120 | , int>::type = 0> |
| 121 | constexpr T pack_get(X&& x, Ts&&... xs) noexcept(BOOST_HOF_IS_NOTHROW_COPY_CONSTRUCTIBLE(T)) |
| 122 | { |
| 123 | return static_cast<T>(boost::hof::alias_value<Tag, T>(BOOST_HOF_FORWARD(X)(x), xs...)); |
| 124 | } |
| 125 | |
| 126 | template<class T, class Tag, class X, class... Ts, typename std::enable_if< |
| 127 | std::is_lvalue_reference<T>::value |
| 128 | , int>::type = 0> |
| 129 | constexpr T pack_get(X&& x, Ts&&... xs) noexcept |
| 130 | { |
| 131 | return boost::hof::alias_value<Tag, T>(x, xs...); |
| 132 | } |
| 133 | |
| 134 | template<class T, class Tag, class X, class... Ts, typename std::enable_if< |
| 135 | !is_copyable<T>::value |
| 136 | , int>::type = 0> |
| 137 | constexpr auto pack_get(X&& x, Ts&&... xs) BOOST_HOF_RETURNS |
| 138 | ( |
| 139 | boost::hof::alias_value<Tag, T>(BOOST_HOF_FORWARD(X)(x), xs...) |
| 140 | ); |
| 141 | |
| 142 | #if (defined(__GNUC__) && !defined (__clang__) && __GNUC__ == 4 && __GNUC_MINOR__ < 7) || defined(_MSC_VER) |
| 143 | template<class... Ts> |
| 144 | struct pack_holder_base |
| 145 | : Ts::type... |
| 146 | { |
| 147 | template<class... Xs, class=typename std::enable_if<(sizeof...(Xs) == sizeof...(Ts))>::type> |
| 148 | constexpr pack_holder_base(Xs&&... xs) |
| 149 | BOOST_HOF_NOEXCEPT(BOOST_HOF_AND_UNPACK(BOOST_HOF_IS_NOTHROW_CONSTRUCTIBLE(typename Ts::type, Xs&&))) |
| 150 | : Ts::type(BOOST_HOF_FORWARD(Xs)(xs))... |
| 151 | {} |
| 152 | #ifndef _MSC_VER |
| 153 | // BOOST_HOF_INHERIT_DEFAULT(pack_holder_base, typename std::remove_cv<typename std::remove_reference<typename Ts::type>::type>::type...) |
| 154 | BOOST_HOF_INHERIT_DEFAULT(pack_holder_base, typename Ts::type...) |
| 155 | #endif |
| 156 | }; |
| 157 | |
| 158 | template<class T> |
| 159 | struct pack_holder_base<T> |
| 160 | : T::type |
| 161 | { |
| 162 | typedef typename T::type base; |
| 163 | BOOST_HOF_INHERIT_CONSTRUCTOR(pack_holder_base, base); |
| 164 | }; |
| 165 | |
| 166 | template<class... Ts> |
| 167 | struct pack_holder_builder |
| 168 | { |
| 169 | template<class T, std::size_t N> |
| 170 | struct apply |
| 171 | : pack_holder<T, pack_tag<seq<N>, Ts...>> |
| 172 | {}; |
| 173 | }; |
| 174 | |
| 175 | template<std::size_t... Ns, class... Ts> |
| 176 | struct pack_base<seq<Ns...>, Ts...> |
| 177 | : pack_holder_base<typename pack_holder_builder<Ts...>::template apply<Ts, Ns>...> |
| 178 | { |
| 179 | typedef pack_holder_base<typename pack_holder_builder<Ts...>::template apply<Ts, Ns>...> base; |
| 180 | template<class X1, class X2, class... Xs> |
| 181 | constexpr pack_base(X1&& x1, X2&& x2, Xs&&... xs) |
| 182 | BOOST_HOF_NOEXCEPT(BOOST_HOF_IS_NOTHROW_CONSTRUCTIBLE(base, X1&&, X2&&, Xs&...)) |
| 183 | : base(BOOST_HOF_FORWARD(X1)(x1), BOOST_HOF_FORWARD(X2)(x2), BOOST_HOF_FORWARD(Xs)(xs)...) |
| 184 | {} |
| 185 | |
| 186 | template<class X1, typename std::enable_if<(BOOST_HOF_IS_CONSTRUCTIBLE(base, X1)), int>::type = 0> |
| 187 | constexpr pack_base(X1&& x1) |
| 188 | BOOST_HOF_NOEXCEPT(BOOST_HOF_IS_NOTHROW_CONSTRUCTIBLE(base, X1&&)) |
| 189 | : base(BOOST_HOF_FORWARD(X1)(x1)) |
| 190 | {} |
| 191 | |
| 192 | // BOOST_HOF_INHERIT_DEFAULT(pack_base, typename std::remove_cv<typename std::remove_reference<Ts>::type>::type...); |
| 193 | BOOST_HOF_INHERIT_DEFAULT(pack_base, Ts...); |
| 194 | |
| 195 | BOOST_HOF_RETURNS_CLASS(pack_base); |
| 196 | |
| 197 | template<class F> |
| 198 | constexpr auto operator()(F&& f) const BOOST_HOF_RETURNS |
| 199 | ( |
| 200 | f(boost::hof::detail::pack_get<Ts, pack_tag<seq<Ns>, Ts...>>(*BOOST_HOF_CONST_THIS, f)...) |
| 201 | ); |
| 202 | |
| 203 | typedef std::integral_constant<std::size_t, sizeof...(Ts)> fit_function_param_limit; |
| 204 | |
| 205 | template<class F> |
| 206 | struct apply |
| 207 | : F::template apply<Ts...> |
| 208 | {}; |
| 209 | }; |
| 210 | |
| 211 | template<class T> |
| 212 | struct pack_base<seq<0>, T> |
| 213 | : pack_holder_base<pack_holder<T, pack_tag<seq<0>, T>>> |
| 214 | { |
| 215 | typedef pack_holder_base<pack_holder<T, pack_tag<seq<0>, T>>> base; |
| 216 | |
| 217 | template<class X1, typename std::enable_if<(BOOST_HOF_IS_CONSTRUCTIBLE(base, X1)), int>::type = 0> |
| 218 | constexpr pack_base(X1&& x1) |
| 219 | BOOST_HOF_NOEXCEPT(BOOST_HOF_IS_NOTHROW_CONSTRUCTIBLE(base, X1&&)) |
| 220 | : base(BOOST_HOF_FORWARD(X1)(x1)) |
| 221 | {} |
| 222 | |
| 223 | BOOST_HOF_INHERIT_DEFAULT(pack_base, T); |
| 224 | |
| 225 | BOOST_HOF_RETURNS_CLASS(pack_base); |
| 226 | |
| 227 | template<class F> |
| 228 | constexpr auto operator()(F&& f) const BOOST_HOF_RETURNS |
| 229 | ( |
| 230 | f(boost::hof::detail::pack_get<T, pack_tag<seq<0>, T>>(*BOOST_HOF_CONST_THIS, f)) |
| 231 | ); |
| 232 | |
| 233 | typedef std::integral_constant<std::size_t, 1> fit_function_param_limit; |
| 234 | |
| 235 | template<class F> |
| 236 | struct apply |
| 237 | : F::template apply<T> |
| 238 | {}; |
| 239 | }; |
| 240 | |
| 241 | #else |
| 242 | |
| 243 | template<std::size_t... Ns, class... Ts> |
| 244 | struct pack_base<seq<Ns...>, Ts...> |
| 245 | : pack_holder<Ts, pack_tag<seq<Ns>, Ts...>>::type... |
| 246 | { |
| 247 | // BOOST_HOF_INHERIT_DEFAULT(pack_base, typename std::remove_cv<typename std::remove_reference<Ts>::type>::type...); |
| 248 | BOOST_HOF_INHERIT_DEFAULT(pack_base, Ts...); |
| 249 | |
| 250 | template<class... Xs, BOOST_HOF_ENABLE_IF_CONVERTIBLE_UNPACK(Xs&&, typename pack_holder<Ts, pack_tag<seq<Ns>, Ts...>>::type)> |
| 251 | constexpr pack_base(Xs&&... xs) |
| 252 | BOOST_HOF_NOEXCEPT(BOOST_HOF_AND_UNPACK(BOOST_HOF_IS_NOTHROW_CONSTRUCTIBLE(typename pack_holder<Ts, pack_tag<seq<Ns>, Ts...>>::type, Xs&&))) |
| 253 | : pack_holder<Ts, pack_tag<seq<Ns>, Ts...>>::type(BOOST_HOF_FORWARD(Xs)(xs))... |
| 254 | {} |
| 255 | |
| 256 | // typedef pack_base<seq<Ns...>, Ts...> self_t; |
| 257 | |
| 258 | BOOST_HOF_RETURNS_CLASS(pack_base); |
| 259 | |
| 260 | template<class F> |
| 261 | constexpr auto operator()(F&& f) const BOOST_HOF_RETURNS |
| 262 | ( |
| 263 | f(boost::hof::detail::pack_get<Ts, pack_tag<seq<Ns>, Ts...>>(*BOOST_HOF_CONST_THIS, f)...) |
| 264 | ); |
| 265 | |
| 266 | typedef std::integral_constant<std::size_t, sizeof...(Ts)> fit_function_param_limit; |
| 267 | |
| 268 | template<class F> |
| 269 | struct apply |
| 270 | : F::template apply<Ts...> |
| 271 | {}; |
| 272 | }; |
| 273 | |
| 274 | #endif |
| 275 | |
| 276 | template<> |
| 277 | struct pack_base<seq<> > |
| 278 | { |
| 279 | template<class F> |
| 280 | constexpr auto operator()(F&& f) const BOOST_HOF_RETURNS |
| 281 | (f()); |
| 282 | |
| 283 | typedef std::integral_constant<std::size_t, 0> fit_function_param_limit; |
| 284 | |
| 285 | template<class F> |
| 286 | struct apply |
| 287 | : F::template apply<> |
| 288 | {}; |
| 289 | }; |
| 290 | |
| 291 | #define BOOST_HOF_DETAIL_UNPACK_PACK_BASE(ref, move) \ |
| 292 | template<class F, std::size_t... Ns, class... Ts> \ |
| 293 | constexpr auto unpack_pack_base(F&& f, pack_base<seq<Ns...>, Ts...> ref x) \ |
| 294 | BOOST_HOF_RETURNS(f(boost::hof::alias_value<pack_tag<seq<Ns>, Ts...>, Ts>(move(x), f)...)) |
| 295 | BOOST_HOF_UNARY_PERFECT_FOREACH(BOOST_HOF_DETAIL_UNPACK_PACK_BASE) |
| 296 | |
| 297 | template<class P1, class P2> |
| 298 | struct pack_join_base; |
| 299 | |
| 300 | // TODO: Extend to join more than two packs at a time |
| 301 | template<std::size_t... Ns1, class... Ts1, std::size_t... Ns2, class... Ts2> |
| 302 | struct pack_join_base<pack_base<seq<Ns1...>, Ts1...>, pack_base<seq<Ns2...>, Ts2...>> |
| 303 | { |
| 304 | static constexpr long total_size = sizeof...(Ts1) + sizeof...(Ts2); |
| 305 | typedef pack_base<typename detail::gens<total_size>::type, Ts1..., Ts2...> result_type; |
| 306 | |
| 307 | template<class P1, class P2> |
| 308 | static constexpr result_type call(P1&& p1, P2&& p2) |
| 309 | BOOST_HOF_RETURNS_DEDUCE_NOEXCEPT( |
| 310 | result_type( |
| 311 | boost::hof::detail::pack_get<Ts1, pack_tag<seq<Ns1>, Ts1...>>(BOOST_HOF_FORWARD(P1)(p1))..., |
| 312 | boost::hof::detail::pack_get<Ts2, pack_tag<seq<Ns2>, Ts2...>>(BOOST_HOF_FORWARD(P2)(p2))...) |
| 313 | ) |
| 314 | { |
| 315 | return result_type( |
| 316 | boost::hof::detail::pack_get<Ts1, pack_tag<seq<Ns1>, Ts1...>>(BOOST_HOF_FORWARD(P1)(p1))..., |
| 317 | boost::hof::detail::pack_get<Ts2, pack_tag<seq<Ns2>, Ts2...>>(BOOST_HOF_FORWARD(P2)(p2))...); |
| 318 | } |
| 319 | }; |
| 320 | |
| 321 | template<class P1, class P2> |
| 322 | struct pack_join_result |
| 323 | : pack_join_base< |
| 324 | typename std::remove_cv<typename std::remove_reference<P1>::type>::type, |
| 325 | typename std::remove_cv<typename std::remove_reference<P2>::type>::type |
| 326 | > |
| 327 | {}; |
| 328 | |
| 329 | |
| 330 | struct pack_basic_f |
| 331 | { |
| 332 | template<class... Ts> |
| 333 | constexpr auto operator()(Ts&&... xs) const BOOST_HOF_RETURNS |
| 334 | ( |
| 335 | pack_base<typename gens<sizeof...(Ts)>::type, typename remove_rvalue_reference<Ts>::type...>(BOOST_HOF_FORWARD(Ts)(xs)...) |
| 336 | ); |
| 337 | }; |
| 338 | |
| 339 | struct pack_forward_f |
| 340 | { |
| 341 | template<class... Ts> |
| 342 | constexpr auto operator()(Ts&&... xs) const BOOST_HOF_RETURNS |
| 343 | ( |
| 344 | pack_base<typename gens<sizeof...(Ts)>::type, Ts&&...>(BOOST_HOF_FORWARD(Ts)(xs)...) |
| 345 | ); |
| 346 | }; |
| 347 | |
| 348 | struct pack_f |
| 349 | { |
| 350 | template<class... Ts> |
| 351 | constexpr auto operator()(Ts&&... xs) const BOOST_HOF_RETURNS |
| 352 | ( |
| 353 | pack_basic_f()(boost::hof::decay(BOOST_HOF_FORWARD(Ts)(xs))...) |
| 354 | ); |
| 355 | }; |
| 356 | |
| 357 | template<class P1, class P2> |
| 358 | constexpr typename pack_join_result<P1, P2>::result_type make_pack_join_dual(P1&& p1, P2&& p2) |
| 359 | BOOST_HOF_RETURNS_DEDUCE_NOEXCEPT(pack_join_result<P1, P2>::call(BOOST_HOF_FORWARD(P1)(p1), BOOST_HOF_FORWARD(P2)(p2))) |
| 360 | { |
| 361 | return pack_join_result<P1, P2>::call(BOOST_HOF_FORWARD(P1)(p1), BOOST_HOF_FORWARD(P2)(p2)); |
| 362 | } |
| 363 | |
| 364 | // Manually compute join return type to make older gcc happy |
| 365 | template<class... Ts> |
| 366 | struct join_type; |
| 367 | |
| 368 | template<class T> |
| 369 | struct join_type<T> |
| 370 | { |
| 371 | typedef T type; |
| 372 | }; |
| 373 | |
| 374 | template<class T, class... Ts> |
| 375 | struct join_type<T, Ts...> |
| 376 | { |
| 377 | typedef typename pack_join_result<T, typename join_type<Ts...>::type>::result_type type; |
| 378 | }; |
| 379 | |
| 380 | template<class P1> |
| 381 | constexpr P1 make_pack_join(P1&& p1) BOOST_HOF_NOEXCEPT_CONSTRUCTIBLE(P1, P1&&) |
| 382 | { |
| 383 | return BOOST_HOF_FORWARD(P1)(p1); |
| 384 | } |
| 385 | |
| 386 | template<class P1, class... Ps> |
| 387 | constexpr typename join_type<P1, Ps...>::type make_pack_join(P1&& p1, Ps&&... ps) |
| 388 | BOOST_HOF_RETURNS_DEDUCE_NOEXCEPT(make_pack_join_dual(BOOST_HOF_FORWARD(P1)(p1), make_pack_join(BOOST_HOF_FORWARD(Ps)(ps)...))) |
| 389 | { |
| 390 | return make_pack_join_dual(BOOST_HOF_FORWARD(P1)(p1), make_pack_join(BOOST_HOF_FORWARD(Ps)(ps)...)); |
| 391 | } |
| 392 | |
| 393 | struct pack_join_f |
| 394 | { |
| 395 | |
| 396 | template<class... Ps> |
| 397 | constexpr auto operator()(Ps&&... ps) const BOOST_HOF_RETURNS |
| 398 | ( |
| 399 | make_pack_join(BOOST_HOF_FORWARD(Ps)(ps)...) |
| 400 | ); |
| 401 | }; |
| 402 | |
| 403 | } |
| 404 | |
| 405 | BOOST_HOF_DECLARE_STATIC_VAR(pack_basic, detail::pack_basic_f); |
| 406 | BOOST_HOF_DECLARE_STATIC_VAR(pack_forward, detail::pack_forward_f); |
| 407 | BOOST_HOF_DECLARE_STATIC_VAR(pack, detail::pack_f); |
| 408 | |
| 409 | BOOST_HOF_DECLARE_STATIC_VAR(pack_join, detail::pack_join_f); |
| 410 | |
| 411 | template<class T, class... Ts> |
| 412 | struct unpack_sequence<detail::pack_base<T, Ts...>> |
| 413 | { |
| 414 | template<class F, class P> |
| 415 | constexpr static auto apply(F&& f, P&& p) BOOST_HOF_RETURNS |
| 416 | ( |
| 417 | boost::hof::detail::unpack_pack_base(BOOST_HOF_FORWARD(F)(f), BOOST_HOF_FORWARD(P)(p)) |
| 418 | ); |
| 419 | }; |
| 420 | |
| 421 | }} // namespace boost::hof |
| 422 | |
| 423 | #endif |
| 424 | |