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>
pack_get(X && x,Ts &&...xs)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>
pack_get(X && x,Ts &&...xs)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>
pack_holder_baseboost::hof::detail::pack_holder_base148     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>
pack_baseboost::hof::detail::pack_base181     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>
pack_baseboost::hof::detail::pack_base187     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>
pack_baseboost::hof::detail::pack_base218     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)>
pack_baseboost::hof::detail::pack_base251     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>
callboost::hof::detail::pack_join_base308     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>
make_pack_join_dual(P1 && p1,P2 && 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>
make_pack_join(P1 && 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>
make_pack_join(P1 && p1,Ps &&...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