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