• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*=============================================================================
2    Copyright (c) 2014 Paul Fultz II
3    proj.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_ON_H
9#define BOOST_HOF_GUARD_FUNCTION_ON_H
10
11/// proj
12/// ====
13///
14/// Description
15/// -----------
16///
17/// The `proj` function adaptor applies a projection onto the parameters of
18/// another function. This is useful, for example, to define a function for
19/// sorting such that the ordering is based off of the value of one of its
20/// member fields.
21///
22/// Also, if just a projection is given, then the projection will be called
23/// for each of its arguments.
24///
25/// Note: All projections are always evaluated in order from left-to-right.
26///
27/// Synopsis
28/// --------
29///
30///     template<class Projection, class F>
31///     constexpr proj_adaptor<Projection, F> proj(Projection p, F f);
32///
33///     template<class Projection>
34///     constexpr proj_adaptor<Projection> proj(Projection p);
35///
36/// Semantics
37/// ---------
38///
39///     assert(proj(p, f)(xs...) == f(p(xs)...));
40///     assert(proj(p)(xs...) == p(xs)...);
41///
42/// Requirements
43/// ------------
44///
45/// Projection must be:
46///
47/// * [UnaryInvocable](UnaryInvocable)
48/// * MoveConstructible
49///
50/// F must be:
51///
52/// * [ConstInvocable](ConstInvocable)
53/// * MoveConstructible
54///
55/// Example
56/// -------
57///
58///     #include <boost/hof.hpp>
59///     #include <cassert>
60///     using namespace boost::hof;
61///
62///     struct foo
63///     {
64///         foo(int x_) : x(x_)
65///         {}
66///         int x;
67///     };
68///
69///     int main() {
70///         assert(boost::hof::proj(&foo::x, _ + _)(foo(1), foo(2)) == 3);
71///     }
72///
73/// References
74/// ----------
75///
76/// * [Projections](Projections)
77/// * [Variadic print](<Variadic print>)
78///
79
80
81
82#include <utility>
83#include <boost/hof/always.hpp>
84#include <boost/hof/detail/callable_base.hpp>
85#include <boost/hof/detail/result_of.hpp>
86#include <boost/hof/detail/move.hpp>
87#include <boost/hof/detail/make.hpp>
88#include <boost/hof/detail/static_const_var.hpp>
89#include <boost/hof/detail/compressed_pair.hpp>
90#include <boost/hof/detail/result_type.hpp>
91#include <boost/hof/apply_eval.hpp>
92
93namespace boost { namespace hof {
94
95namespace detail {
96
97template<class T, class Projection>
98struct project_eval
99{
100    T&& x;
101    const Projection& p;
102
103    template<class X, class P>
104    constexpr project_eval(X&& xp, const P& pp) : x(BOOST_HOF_FORWARD(X)(xp)), p(pp)
105    {}
106
107    constexpr auto operator()() const BOOST_HOF_RETURNS
108    (p(BOOST_HOF_FORWARD(T)(x)));
109};
110
111template<class T, class Projection>
112constexpr project_eval<T, Projection> make_project_eval(T&& x, const Projection& p)
113{
114    return project_eval<T, Projection>(BOOST_HOF_FORWARD(T)(x), p);
115}
116
117template<class T, class Projection>
118struct project_void_eval
119{
120    T&& x;
121    const Projection& p;
122
123    template<class X, class P>
124    constexpr project_void_eval(X&& xp, const P& pp) : x(BOOST_HOF_FORWARD(X)(xp)), p(pp)
125    {}
126
127    struct void_ {};
128
129    constexpr void_ operator()() const
130    {
131        return p(BOOST_HOF_FORWARD(T)(x)), void_();
132    }
133};
134
135template<class T, class Projection>
136constexpr project_void_eval<T, Projection> make_project_void_eval(T&& x, const Projection& p)
137{
138    return project_void_eval<T, Projection>(BOOST_HOF_FORWARD(T)(x), p);
139}
140
141template<class Projection, class F, class... Ts,
142    class R=decltype(
143        std::declval<const F&>()(std::declval<const Projection&>()(std::declval<Ts>())...)
144    )>
145constexpr R by_eval(const Projection& p, const F& f, Ts&&... xs)
146{
147    return boost::hof::apply_eval(f, make_project_eval(BOOST_HOF_FORWARD(Ts)(xs), p)...);
148}
149
150#if BOOST_HOF_NO_ORDERED_BRACE_INIT
151#define BOOST_HOF_BY_VOID_RETURN BOOST_HOF_ALWAYS_VOID_RETURN
152#else
153#if BOOST_HOF_NO_CONSTEXPR_VOID
154#define BOOST_HOF_BY_VOID_RETURN boost::hof::detail::swallow
155#else
156#define BOOST_HOF_BY_VOID_RETURN void
157#endif
158#endif
159
160template<class Projection, class... Ts>
161constexpr BOOST_HOF_ALWAYS_VOID_RETURN by_void_eval(const Projection& p, Ts&&... xs)
162{
163    return boost::hof::apply_eval(boost::hof::always(), boost::hof::detail::make_project_void_eval(BOOST_HOF_FORWARD(Ts)(xs), p)...);
164}
165
166struct swallow
167{
168    template<class... Ts>
169    constexpr swallow(Ts&&...)
170    {}
171};
172
173}
174
175template<class Projection, class F=void>
176struct proj_adaptor;
177
178template<class Projection, class F>
179struct proj_adaptor : detail::compressed_pair<detail::callable_base<Projection>, detail::callable_base<F>>, detail::function_result_type<F>
180{
181    typedef proj_adaptor fit_rewritable_tag;
182    typedef detail::compressed_pair<detail::callable_base<Projection>, detail::callable_base<F>> base;
183    template<class... Ts>
184    constexpr const detail::callable_base<F>& base_function(Ts&&... xs) const
185    {
186        return this->second(xs...);;
187    }
188
189    template<class... Ts>
190    constexpr const detail::callable_base<Projection>& base_projection(Ts&&... xs) const
191    {
192        return this->first(xs...);
193    }
194
195    struct by_failure
196    {
197        template<class Failure>
198        struct apply
199        {
200            template<class... Ts>
201            struct of
202            : Failure::template of<decltype(std::declval<detail::callable_base<Projection>>()(std::declval<Ts>()))...>
203            {};
204        };
205    };
206
207    struct failure
208    : failure_map<by_failure, detail::callable_base<F>>
209    {};
210
211    BOOST_HOF_INHERIT_CONSTRUCTOR(proj_adaptor, base)
212
213    BOOST_HOF_RETURNS_CLASS(proj_adaptor);
214
215    template<class... Ts>
216    constexpr BOOST_HOF_SFINAE_RESULT(const detail::callable_base<F>&, result_of<const detail::callable_base<Projection>&, id_<Ts>>...)
217    operator()(Ts&&... xs) const BOOST_HOF_SFINAE_RETURNS
218    (
219        boost::hof::detail::by_eval(
220            BOOST_HOF_MANGLE_CAST(const detail::callable_base<Projection>&)(BOOST_HOF_CONST_THIS->base_projection(xs...)),
221            BOOST_HOF_MANGLE_CAST(const detail::callable_base<F>&)(BOOST_HOF_CONST_THIS->base_function(xs...)),
222            BOOST_HOF_FORWARD(Ts)(xs)...
223        )
224    );
225};
226
227template<class Projection>
228struct proj_adaptor<Projection, void> : detail::callable_base<Projection>
229{
230    typedef proj_adaptor fit_rewritable1_tag;
231    template<class... Ts>
232    constexpr const detail::callable_base<Projection>& base_projection(Ts&&... xs) const
233    {
234        return boost::hof::always_ref(*this)(xs...);
235    }
236
237    BOOST_HOF_INHERIT_DEFAULT(proj_adaptor, detail::callable_base<Projection>)
238
239    template<class P, BOOST_HOF_ENABLE_IF_CONVERTIBLE(P, detail::callable_base<Projection>)>
240    constexpr proj_adaptor(P&& p)
241    : detail::callable_base<Projection>(BOOST_HOF_FORWARD(P)(p))
242    {}
243
244    BOOST_HOF_RETURNS_CLASS(proj_adaptor);
245
246    template<class... Ts, class=detail::holder<decltype(std::declval<Projection>()(std::declval<Ts>()))...>>
247    constexpr BOOST_HOF_BY_VOID_RETURN operator()(Ts&&... xs) const
248    {
249#if BOOST_HOF_NO_ORDERED_BRACE_INIT
250        return boost::hof::detail::by_void_eval(this->base_projection(xs...), BOOST_HOF_FORWARD(Ts)(xs)...);
251#else
252#if BOOST_HOF_NO_CONSTEXPR_VOID
253        return
254#endif
255        boost::hof::detail::swallow{
256            (this->base_projection(xs...)(BOOST_HOF_FORWARD(Ts)(xs)), 0)...
257        };
258#endif
259    }
260};
261
262BOOST_HOF_DECLARE_STATIC_VAR(proj, detail::make<proj_adaptor>);
263
264}} // namespace boost::hof
265#endif
266