• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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