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 
81 namespace boost { namespace hof {
82 
83 namespace detail {
84 
85 struct placeholder_transformer
86 {
87     template<class T, typename std::enable_if<(std::is_placeholder<T>::value > 0), int>::type = 0>
operator ()boost::hof::detail::placeholder_transformer88     constexpr detail::make_args_f<std::size_t, std::is_placeholder<T>::value> operator()(const T&) const noexcept
89     {
90         return {};
91     }
92 };
93 
94 struct bind_transformer
95 {
96     template<class T, typename std::enable_if<std::is_bind_expression<T>::value, int>::type = 0>
operator ()boost::hof::detail::bind_transformer97     constexpr const T& operator()(const T& x) const noexcept
98     {
99         return x;
100     }
101 };
102 
103 struct 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 
110 struct 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 
117 BOOST_HOF_DECLARE_STATIC_VAR(pick_transformer, first_of_adaptor<placeholder_transformer, bind_transformer, ref_transformer, id_transformer>);
118 
119 template<class T, class Pack>
120 constexpr 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 
125 template<class F, class Pack>
126 struct lazy_unpack
127 {
128     const F& f;
129     const Pack& p;
130 
lazy_unpackboost::hof::detail::lazy_unpack131     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 
142 template<class F, class Pack>
make_lazy_unpack(const F & f,const Pack & p)143 constexpr 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 
148 template<class F, class Pack>
149 struct 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
BOOST_HOF_INHERIT_CONSTRUCTORboost::hof::detail::lazy_invoker156     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>
get_packboost::hof::detail::lazy_invoker176     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 
195 template<class F, class Pack>
make_lazy_invoker(F f,Pack pack)196 constexpr lazy_invoker<F, Pack> make_lazy_invoker(F f, Pack pack)
197 BOOST_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 
202 template<class F>
203 struct lazy_nullary_invoker : F
204 {
205     BOOST_HOF_INHERIT_CONSTRUCTOR(lazy_nullary_invoker, F);
206 
207     template<class... Ts>
base_functionboost::hof::detail::lazy_nullary_invoker208     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 
222 template<class F>
make_lazy_nullary_invoker(F f)223 constexpr lazy_nullary_invoker<F> make_lazy_nullary_invoker(F f)
224 BOOST_HOF_NOEXCEPT_CONSTRUCTIBLE(lazy_nullary_invoker<F>, F&&)
225 {
226     return lazy_nullary_invoker<F>(static_cast<F&&>(f));
227 }
228 }
229 
230 
231 template<class F>
232 struct lazy_adaptor : detail::callable_base<F>
233 {
234     BOOST_HOF_INHERIT_CONSTRUCTOR(lazy_adaptor, detail::callable_base<F>);
235 
236     template<class... Ts>
base_functionboost::hof::lazy_adaptor237     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>
operator ()boost::hof::lazy_adaptor253     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 
283 BOOST_HOF_DECLARE_STATIC_VAR(lazy, detail::make<lazy_adaptor>);
284 
285 }} // namespace boost::hof
286 
287 namespace 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