• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*=============================================================================
2    Copyright (c) 2014 Paul Fultz II
3    lazy.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_LAZY_H
9#define BOOST_HOF_GUARD_FUNCTION_LAZY_H
10
11/// lazy
12/// ====
13///
14/// Description
15/// -----------
16///
17/// The `lazy` function adaptor returns a function object call wrapper for a
18/// function. Calling this wrapper is equivalent to invoking the function. It
19/// is a simple form of lambda expressions, but is constexpr friendly. By
20/// default, `lazy` captures all of its variables by value, just like `bind`.
21/// `std::ref` can be used to capture references instead.
22///
23/// Ultimately, calling `lazy(f)(x)` is the equivalent to calling
24/// `std::bind(f, x)` except the lazy version can be called in a constexpr
25/// context, as well. The `lazy` adaptor is compatible with `std::bind`, so
26/// most of the time `lazy` and `std::bind` can be used interchangeably.
27///
28/// Synopsis
29/// --------
30///
31///     template<class F>
32///     constexpr lazy_adaptor<F> lazy(F f);
33///
34/// Semantics
35/// ---------
36///
37///     assert(lazy(f)(xs...) == std::bind(f, xs...))
38///     assert(lazy(f)(xs...)() == f(xs...))
39///     assert(lazy(f)(_1)(x) == f(x))
40///     assert(lazy(f)(lazy(g)(_1))(x) == f(g(x)))
41///
42/// Requirements
43/// ------------
44///
45/// F must be:
46///
47/// * [ConstInvocable](ConstInvocable)
48/// * MoveConstructible
49///
50/// Example
51/// -------
52///
53///     #include <boost/hof.hpp>
54///     #include <cassert>
55///     using namespace boost::hof;
56///
57///     int main() {
58///         auto add = [](auto x, auto y) { return x+y; };
59///         auto increment = lazy(add)(_1, 1);
60///         assert(increment(5) == 6);
61///     }
62///
63/// References
64/// ----------
65///
66/// * [std::bind](http://en.cppreference.com/w/cpp/utility/functional/bind)
67///
68
69#include <boost/hof/arg.hpp>
70#include <boost/hof/first_of.hpp>
71#include <boost/hof/always.hpp>
72#include <boost/hof/static.hpp>
73#include <boost/hof/detail/delegate.hpp>
74#include <boost/hof/detail/compressed_pair.hpp>
75#include <boost/hof/pack.hpp>
76#include <boost/hof/detail/make.hpp>
77#include <boost/hof/detail/static_const_var.hpp>
78#include <functional>
79#include <type_traits>
80
81namespace boost { namespace hof {
82
83namespace detail {
84
85struct placeholder_transformer
86{
87    template<class T, typename std::enable_if<(std::is_placeholder<T>::value > 0), int>::type = 0>
88    constexpr detail::make_args_f<std::size_t, std::is_placeholder<T>::value> operator()(const T&) const noexcept
89    {
90        return {};
91    }
92};
93
94struct bind_transformer
95{
96    template<class T, typename std::enable_if<std::is_bind_expression<T>::value, int>::type = 0>
97    constexpr const T& operator()(const T& x) const noexcept
98    {
99        return x;
100    }
101};
102
103struct ref_transformer
104{
105    template<class T>
106    constexpr auto operator()(std::reference_wrapper<T> x) const
107    BOOST_HOF_SFINAE_RETURNS(boost::hof::always_ref(x.get()));
108};
109
110struct id_transformer
111{
112    template<class T>
113    constexpr auto operator()(T&& x) const
114    BOOST_HOF_SFINAE_RETURNS(always_detail::always_base<T>(BOOST_HOF_FORWARD(T)(x)));
115};
116
117BOOST_HOF_DECLARE_STATIC_VAR(pick_transformer, first_of_adaptor<placeholder_transformer, bind_transformer, ref_transformer, id_transformer>);
118
119template<class T, class Pack>
120constexpr auto lazy_transform(T&& x, const Pack& p) BOOST_HOF_RETURNS
121(
122    p(boost::hof::detail::pick_transformer(BOOST_HOF_FORWARD(T)(x)))
123);
124
125template<class F, class Pack>
126struct lazy_unpack
127{
128    const F& f;
129    const Pack& p;
130
131    constexpr lazy_unpack(const F& fp, const Pack& pp) noexcept
132    : f(fp), p(pp)
133    {}
134
135    template<class... Ts>
136    constexpr auto operator()(Ts&&... xs) const BOOST_HOF_RETURNS
137    (
138        f(lazy_transform(BOOST_HOF_FORWARD(Ts)(xs), p)...)
139    );
140};
141
142template<class F, class Pack>
143constexpr lazy_unpack<F, Pack> make_lazy_unpack(const F& f, const Pack& p) noexcept
144{
145    return lazy_unpack<F, Pack>(f, p);
146}
147
148template<class F, class Pack>
149struct lazy_invoker
150: detail::compressed_pair<F, Pack>
151{
152    typedef detail::compressed_pair<F, Pack> base_type;
153    typedef lazy_invoker fit_rewritable1_tag;
154
155#ifdef _MSC_VER
156    BOOST_HOF_INHERIT_CONSTRUCTOR(lazy_invoker, base_type)
157#else
158    BOOST_HOF_INHERIT_DEFAULT_EMPTY(lazy_invoker, base_type)
159
160    template<class X, class Y,
161        BOOST_HOF_ENABLE_IF_CONSTRUCTIBLE(base_type, X&&, Y&&)
162    >
163    constexpr lazy_invoker(X&& x, Y&& y)
164    BOOST_HOF_NOEXCEPT_CONSTRUCTIBLE(base_type, X&&, Y&&)
165    : base_type(BOOST_HOF_FORWARD(X)(x), BOOST_HOF_FORWARD(Y)(y))
166    {}
167#endif
168
169    template<class... Ts>
170    constexpr const F& base_function(Ts&&... xs) const noexcept
171    {
172        return this->first(xs...);
173    }
174
175    template<class... Ts>
176    constexpr const Pack& get_pack(Ts&&... xs) const noexcept
177    {
178        return this->second(xs...);
179    }
180
181    BOOST_HOF_RETURNS_CLASS(lazy_invoker);
182
183    template<class... Ts>
184    constexpr auto operator()(Ts&&... xs) const BOOST_HOF_RETURNS
185    (
186        BOOST_HOF_MANGLE_CAST(const Pack&)(BOOST_HOF_CONST_THIS->get_pack(xs...))(
187            boost::hof::detail::make_lazy_unpack(
188                BOOST_HOF_MANGLE_CAST(const F&)(BOOST_HOF_CONST_THIS->base_function(xs...)),
189                boost::hof::pack_forward(BOOST_HOF_FORWARD(Ts)(xs)...)
190            )
191        )
192    );
193};
194
195template<class F, class Pack>
196constexpr lazy_invoker<F, Pack> make_lazy_invoker(F f, Pack pack)
197BOOST_HOF_NOEXCEPT_CONSTRUCTIBLE(lazy_invoker<F, Pack>, F&&, Pack&&)
198{
199    return lazy_invoker<F, Pack>(static_cast<F&&>(f), static_cast<Pack&&>(pack));
200}
201
202template<class F>
203struct lazy_nullary_invoker : F
204{
205    BOOST_HOF_INHERIT_CONSTRUCTOR(lazy_nullary_invoker, F);
206
207    template<class... Ts>
208    constexpr const F& base_function(Ts&&... xs) const noexcept
209    {
210        return boost::hof::always_ref(*this)(xs...);
211    }
212
213    BOOST_HOF_RETURNS_CLASS(lazy_nullary_invoker);
214
215    template<class... Ts>
216    constexpr auto operator()(Ts&&... xs) const BOOST_HOF_RETURNS
217    (
218        BOOST_HOF_MANGLE_CAST(const F&)(BOOST_HOF_CONST_THIS->base_function(xs...))()
219    );
220};
221
222template<class F>
223constexpr lazy_nullary_invoker<F> make_lazy_nullary_invoker(F f)
224BOOST_HOF_NOEXCEPT_CONSTRUCTIBLE(lazy_nullary_invoker<F>, F&&)
225{
226    return lazy_nullary_invoker<F>(static_cast<F&&>(f));
227}
228}
229
230
231template<class F>
232struct lazy_adaptor : detail::callable_base<F>
233{
234    BOOST_HOF_INHERIT_CONSTRUCTOR(lazy_adaptor, detail::callable_base<F>);
235
236    template<class... Ts>
237    constexpr const detail::callable_base<F>& base_function(Ts&&... xs) const noexcept
238    {
239        return boost::hof::always_ref(*this)(xs...);
240    }
241
242    BOOST_HOF_RETURNS_CLASS(lazy_adaptor);
243
244    template<class T, class... Ts>
245    constexpr auto operator()(T x, Ts... xs) const BOOST_HOF_RETURNS
246    (
247        boost::hof::detail::make_lazy_invoker(BOOST_HOF_RETURNS_C_CAST(detail::callable_base<F>&&)(BOOST_HOF_CONST_THIS->base_function(x, xs...)),
248            boost::hof::pack_basic(BOOST_HOF_RETURNS_STATIC_CAST(T&&)(x), BOOST_HOF_RETURNS_STATIC_CAST(Ts&&)(xs)...))
249    );
250
251    // Workaround for gcc 4.7
252    template<class Unused=int>
253    constexpr detail::lazy_nullary_invoker<F> operator()() const
254    BOOST_HOF_RETURNS_DEDUCE_NOEXCEPT(
255        boost::hof::detail::make_lazy_nullary_invoker(BOOST_HOF_RETURNS_C_CAST(detail::callable_base<F>&&)(
256            BOOST_HOF_CONST_THIS->base_function(BOOST_HOF_RETURNS_CONSTRUCT(Unused)())
257        ))
258    )
259    {
260        return boost::hof::detail::make_lazy_nullary_invoker((detail::callable_base<F>&&)(
261            this->base_function(Unused())
262        ));
263    }
264
265    // TODO: Overloads to use with ref qualifiers
266
267    // template<class... Ts>
268    // constexpr auto operator()(Ts... xs) const& BOOST_HOF_RETURNS
269    // (
270    //     boost::hof::detail::make_lazy_invoker(this->base_function(xs...),
271    //         pack(boost::hof::move(xs)...))
272    // );
273
274    // template<class... Ts>
275    // constexpr auto operator()(Ts... xs) && BOOST_HOF_RETURNS
276    // (
277    //     boost::hof::detail::make_lazy_invoker((F&&)this->base_function(xs...),
278    //         pack(boost::hof::move(xs)...))
279    // );
280
281};
282
283BOOST_HOF_DECLARE_STATIC_VAR(lazy, detail::make<lazy_adaptor>);
284
285}} // namespace boost::hof
286
287namespace std {
288    template<class F, class Pack>
289    struct is_bind_expression<boost::hof::detail::lazy_invoker<F, Pack>>
290    : std::true_type
291    {};
292
293    template<class F>
294    struct is_bind_expression<boost::hof::detail::lazy_nullary_invoker<F>>
295    : std::true_type
296    {};
297}
298
299#endif
300