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