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 87namespace boost { namespace hof { namespace detail { 88 89template<class...> 90struct pack_tag 91{}; 92 93template<class T, class Tag> 94struct pack_holder 95: detail::alias_empty<T, Tag> 96{}; 97 98template<class Seq, class... Ts> 99struct pack_base; 100 101template<class T> 102struct is_copyable 103: std::integral_constant<bool, ( 104 BOOST_HOF_IS_CONSTRUCTIBLE(T, const T&) 105)> 106{}; 107 108template<class T> 109struct is_copyable<T&> 110: std::true_type 111{}; 112 113template<class T> 114struct is_copyable<T&&> 115: std::true_type 116{}; 117 118template<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> 121constexpr 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 126template<class T, class Tag, class X, class... Ts, typename std::enable_if< 127 std::is_lvalue_reference<T>::value 128, int>::type = 0> 129constexpr T pack_get(X&& x, Ts&&... xs) noexcept 130{ 131 return boost::hof::alias_value<Tag, T>(x, xs...); 132} 133 134template<class T, class Tag, class X, class... Ts, typename std::enable_if< 135 !is_copyable<T>::value 136, int>::type = 0> 137constexpr 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) 143template<class... Ts> 144struct 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 158template<class T> 159struct 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 166template<class... Ts> 167struct 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 175template<std::size_t... Ns, class... Ts> 176struct 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 211template<class T> 212struct 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 243template<std::size_t... Ns, class... Ts> 244struct 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 276template<> 277struct 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) \ 292template<class F, std::size_t... Ns, class... Ts> \ 293constexpr auto unpack_pack_base(F&& f, pack_base<seq<Ns...>, Ts...> ref x) \ 294BOOST_HOF_RETURNS(f(boost::hof::alias_value<pack_tag<seq<Ns>, Ts...>, Ts>(move(x), f)...)) 295BOOST_HOF_UNARY_PERFECT_FOREACH(BOOST_HOF_DETAIL_UNPACK_PACK_BASE) 296 297template<class P1, class P2> 298struct pack_join_base; 299 300// TODO: Extend to join more than two packs at a time 301template<std::size_t... Ns1, class... Ts1, std::size_t... Ns2, class... Ts2> 302struct 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 321template<class P1, class P2> 322struct 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 330struct 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 339struct 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 348struct 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 357template<class P1, class P2> 358constexpr typename pack_join_result<P1, P2>::result_type make_pack_join_dual(P1&& p1, P2&& p2) 359BOOST_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 365template<class... Ts> 366struct join_type; 367 368template<class T> 369struct join_type<T> 370{ 371 typedef T type; 372}; 373 374template<class T, class... Ts> 375struct join_type<T, Ts...> 376{ 377 typedef typename pack_join_result<T, typename join_type<Ts...>::type>::result_type type; 378}; 379 380template<class P1> 381constexpr P1 make_pack_join(P1&& p1) BOOST_HOF_NOEXCEPT_CONSTRUCTIBLE(P1, P1&&) 382{ 383 return BOOST_HOF_FORWARD(P1)(p1); 384} 385 386template<class P1, class... Ps> 387constexpr typename join_type<P1, Ps...>::type make_pack_join(P1&& p1, Ps&&... ps) 388BOOST_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 393struct 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 405BOOST_HOF_DECLARE_STATIC_VAR(pack_basic, detail::pack_basic_f); 406BOOST_HOF_DECLARE_STATIC_VAR(pack_forward, detail::pack_forward_f); 407BOOST_HOF_DECLARE_STATIC_VAR(pack, detail::pack_f); 408 409BOOST_HOF_DECLARE_STATIC_VAR(pack_join, detail::pack_join_f); 410 411template<class T, class... Ts> 412struct 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