diff options
Diffstat (limited to 'boost/hana/functional')
-rw-r--r-- | boost/hana/functional/always.hpp | 64 | ||||
-rw-r--r-- | boost/hana/functional/apply.hpp | 85 | ||||
-rw-r--r-- | boost/hana/functional/arg.hpp | 141 | ||||
-rw-r--r-- | boost/hana/functional/capture.hpp | 112 | ||||
-rw-r--r-- | boost/hana/functional/compose.hpp | 108 | ||||
-rw-r--r-- | boost/hana/functional/curry.hpp | 169 | ||||
-rw-r--r-- | boost/hana/functional/demux.hpp | 269 | ||||
-rw-r--r-- | boost/hana/functional/fix.hpp | 83 | ||||
-rw-r--r-- | boost/hana/functional/flip.hpp | 73 | ||||
-rw-r--r-- | boost/hana/functional/id.hpp | 38 | ||||
-rw-r--r-- | boost/hana/functional/infix.hpp | 184 | ||||
-rw-r--r-- | boost/hana/functional/iterate.hpp | 201 | ||||
-rw-r--r-- | boost/hana/functional/lockstep.hpp | 114 | ||||
-rw-r--r-- | boost/hana/functional/on.hpp | 83 | ||||
-rw-r--r-- | boost/hana/functional/overload.hpp | 88 | ||||
-rw-r--r-- | boost/hana/functional/overload_linearly.hpp | 105 | ||||
-rw-r--r-- | boost/hana/functional/partial.hpp | 107 | ||||
-rw-r--r-- | boost/hana/functional/placeholder.hpp | 263 | ||||
-rw-r--r-- | boost/hana/functional/reverse_partial.hpp | 101 |
19 files changed, 2388 insertions, 0 deletions
diff --git a/boost/hana/functional/always.hpp b/boost/hana/functional/always.hpp new file mode 100644 index 0000000000..249233d3f2 --- /dev/null +++ b/boost/hana/functional/always.hpp @@ -0,0 +1,64 @@ +/*! +@file +Defines `boost::hana::always`. + +@copyright Louis Dionne 2013-2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_FUNCTIONAL_ALWAYS_HPP +#define BOOST_HANA_FUNCTIONAL_ALWAYS_HPP + +#include <boost/hana/config.hpp> +#include <boost/hana/detail/create.hpp> + +#include <utility> + + +BOOST_HANA_NAMESPACE_BEGIN + //! @ingroup group-functional + //! Return a constant function returning `x` regardless of the + //! argument(s) it is invoked with. + //! + //! Specifically, `always(x)` is a function such that + //! @code + //! always(x)(y...) == x + //! @endcode + //! for any `y...`. A copy of `x` is made and it is owned by the + //! `always(x)` function. When `always(x)` is called, it will return + //! a reference to the `x` it owns. This reference is valid as long + //! as `always(x)` is in scope. + //! + //! + //! ### Example + //! @include example/functional/always.cpp +#ifdef BOOST_HANA_DOXYGEN_INVOKED + constexpr auto always = [](auto&& x) { + return [perfect-capture](auto const& ...y) -> decltype(auto) { + return forwarded(x); + }; + }; +#else + template <typename T> + struct _always { + T val_; + + template <typename ...Args> + constexpr T const& operator()(Args const& ...) const& + { return val_; } + + template <typename ...Args> + constexpr T& operator()(Args const& ...) & + { return val_; } + + template <typename ...Args> + constexpr T operator()(Args const& ...) && + { return std::move(val_); } + }; + + constexpr detail::create<_always> always{}; +#endif +BOOST_HANA_NAMESPACE_END + +#endif // !BOOST_HANA_FUNCTIONAL_ALWAYS_HPP diff --git a/boost/hana/functional/apply.hpp b/boost/hana/functional/apply.hpp new file mode 100644 index 0000000000..75a7f9483d --- /dev/null +++ b/boost/hana/functional/apply.hpp @@ -0,0 +1,85 @@ +/*! +@file +Defines `boost::hana::apply`. + +@copyright Louis Dionne 2013-2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_FUNCTIONAL_APPLY_HPP +#define BOOST_HANA_FUNCTIONAL_APPLY_HPP + +#include <boost/hana/config.hpp> + + +BOOST_HANA_NAMESPACE_BEGIN + //! @ingroup group-functional + //! Invokes a Callable with the given arguments. + //! + //! This is equivalent to [std::invoke][1] that will be added in C++17. + //! However, `apply` is a function object instead of a function, which + //! makes it possible to pass it to higher-order algorithms. + //! + //! + //! @param f + //! A [Callable][2] to be invoked with the given arguments. + //! + //! @param x... + //! The arguments to call `f` with. The number of `x...` must match the + //! arity of `f`. + //! + //! + //! Example + //! ------- + //! @include example/functional/apply.cpp + //! + //! [1]: http://en.cppreference.com/w/cpp/utility/functional/invoke + //! [2]: http://en.cppreference.com/w/cpp/concept/Callable +#ifdef BOOST_HANA_DOXYGEN_INVOKED + constexpr auto apply = [](auto&& f, auto&& ...x) -> decltype(auto) { + return forwarded(f)(forwarded(x)...); + }; +#else + struct apply_t { + template <typename F, typename... Args> + constexpr auto operator()(F&& f, Args&&... args) const -> + decltype(static_cast<F&&>(f)(static_cast<Args&&>(args)...)) + { + return static_cast<F&&>(f)(static_cast<Args&&>(args)...); + } + + template <typename Base, typename T, typename Derived> + constexpr auto operator()(T Base::*pmd, Derived&& ref) const -> + decltype(static_cast<Derived&&>(ref).*pmd) + { + return static_cast<Derived&&>(ref).*pmd; + } + + template <typename PMD, typename Pointer> + constexpr auto operator()(PMD pmd, Pointer&& ptr) const -> + decltype((*static_cast<Pointer&&>(ptr)).*pmd) + { + return (*static_cast<Pointer&&>(ptr)).*pmd; + } + + template <typename Base, typename T, typename Derived, typename... Args> + constexpr auto operator()(T Base::*pmf, Derived&& ref, Args&&... args) const -> + decltype((static_cast<Derived&&>(ref).*pmf)(static_cast<Args&&>(args)...)) + { + return (static_cast<Derived&&>(ref).*pmf)(static_cast<Args&&>(args)...); + } + + template <typename PMF, typename Pointer, typename... Args> + constexpr auto operator()(PMF pmf, Pointer&& ptr, Args&& ...args) const -> + decltype(((*static_cast<Pointer&&>(ptr)).*pmf)(static_cast<Args&&>(args)...)) + { + return ((*static_cast<Pointer&&>(ptr)).*pmf)(static_cast<Args&&>(args)...); + } + }; + + constexpr apply_t apply{}; +#endif +BOOST_HANA_NAMESPACE_END + +#endif // !BOOST_HANA_FUNCTIONAL_APPLY_HPP diff --git a/boost/hana/functional/arg.hpp b/boost/hana/functional/arg.hpp new file mode 100644 index 0000000000..c9c68f0eae --- /dev/null +++ b/boost/hana/functional/arg.hpp @@ -0,0 +1,141 @@ +/*! +@file +Defines `boost::hana::arg`. + +@copyright Louis Dionne 2013-2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_FUNCTIONAL_ARG_HPP +#define BOOST_HANA_FUNCTIONAL_ARG_HPP + +#include <boost/hana/config.hpp> + +#include <cstddef> +#include <type_traits> + + +BOOST_HANA_NAMESPACE_BEGIN + //! @ingroup group-functional + //! Return the `n`th passed argument. + //! + //! Specifically, `arg<n>(x1, ..., xn, ..., xm)` is equivalent to `xn`. + //! Note that indexing starts at 1, so `arg<1>` returns the 1st argument, + //! `arg<2>` the 2nd and so on. Using `arg<0>` is an error. Passing + //! less than `n` arguments to `arg<n>` is also an error. + //! + //! + //! @tparam n + //! An unsigned integer representing the argument to return. `n` must be + //! positive (meaning nonzero). + //! + //! @param x1, ..., xm + //! A variadic pack of arguments from which the `n`th one is returned. + //! + //! + //! @internal + //! ### Discussion: could `n` be dynamic? + //! We could have chosen `arg` to be used like `arg(n)(x...)` instead of + //! `arg<n>(x...)`. Provided all the arguments were of the same type, it + //! would then be possible for `n` to only be known at runtime. However, + //! we would then lose the ability to assert the in-boundedness of `n` + //! statically. + //! + //! ### Rationale for `n` being a non-type template parameter + //! I claim that the only interesting use case is with a compile-time + //! `n`, which means that the usage would become `arg(int_<n>)(x...)`, + //! which is more cumbersome to write than `arg<n>(x...)`. This is open + //! for discussion. + //! @endinternal + //! + //! ### Example + //! @include example/functional/arg.cpp +#ifdef BOOST_HANA_DOXYGEN_INVOKED + template <std::size_t n> + constexpr auto arg = [](auto&& x1, ..., auto&& xm) -> decltype(auto) { + return forwarded(xn); + }; +#else + template <std::size_t n, typename = void> + struct arg_t; + + template <> + struct arg_t<1> { + template <typename X1, typename ...Xn> + constexpr X1 operator()(X1&& x1, Xn&& ...) const + { return static_cast<X1&&>(x1); } + }; + + template <> + struct arg_t<2> { + template <typename X1, typename X2, typename ...Xn> + constexpr X2 operator()(X1&&, X2&& x2, Xn&& ...) const + { return static_cast<X2&&>(x2); } + }; + + template <> + struct arg_t<3> { + template <typename X1, typename X2, typename X3, typename ...Xn> + constexpr X3 operator()(X1&&, X2&&, X3&& x3, Xn&& ...) const + { return static_cast<X3&&>(x3); } + }; + + template <> + struct arg_t<4> { + template <typename X1, typename X2, typename X3, typename X4, typename ...Xn> + constexpr X4 operator()(X1&&, X2&&, X3&&, X4&& x4, Xn&& ...) const + { return static_cast<X4&&>(x4); } + }; + + template <> + struct arg_t<5> { + template <typename X1, typename X2, typename X3, typename X4, + typename X5, typename ...Xn> + constexpr X5 operator()(X1&&, X2&&, X3&&, X4&&, X5&& x5, Xn&& ...) const + { return static_cast<X5&&>(x5); } + }; + + template <std::size_t n, typename> + struct arg_t { + static_assert(n > 0, + "invalid usage of boost::hana::arg<n> with n == 0"); + + template <typename X1, typename X2, typename X3, typename X4, + typename X5, typename ...Xn> + constexpr decltype(auto) + operator()(X1&&, X2&&, X3&&, X4&&, X5&&, Xn&& ...xn) const { + static_assert(sizeof...(xn) >= n - 5, + "invalid usage of boost::hana::arg<n> with too few arguments"); + + // Since compilers will typically try to continue for a bit after + // an error/static assertion, we must avoid sending the compiler + // in a very long computation if n == 0. + return arg_t<n == 0 ? 1 : n - 5>{}(static_cast<Xn&&>(xn)...); + } + }; + + template <std::size_t n> + struct arg_t<n, std::enable_if_t<(n > 25)>> { + template < + typename X1, typename X2, typename X3, typename X4, typename X5, + typename X6, typename X7, typename X8, typename X9, typename X10, + typename X11, typename X12, typename X13, typename X14, typename X15, + typename X16, typename X17, typename X18, typename X19, typename X20, + typename X21, typename X22, typename X23, typename X24, typename X25, + typename ...Xn> + constexpr decltype(auto) + operator()(X1&&, X2&&, X3&&, X4&&, X5&&, + X6&&, X7&&, X8&&, X9&&, X10&&, + X11&&, X12&&, X13&&, X14&&, X15&&, + X16&&, X17&&, X18&&, X19&&, X20&&, + X21&&, X22&&, X23&&, X24&&, X25&&, Xn&& ...xn) const + { return arg_t<n - 25>{}(static_cast<Xn&&>(xn)...); } + }; + + template <std::size_t n> + constexpr arg_t<n> arg{}; +#endif +BOOST_HANA_NAMESPACE_END + +#endif // !BOOST_HANA_FUNCTIONAL_ARG_HPP diff --git a/boost/hana/functional/capture.hpp b/boost/hana/functional/capture.hpp new file mode 100644 index 0000000000..0b5a4677d8 --- /dev/null +++ b/boost/hana/functional/capture.hpp @@ -0,0 +1,112 @@ +/*! +@file +Defines `boost::hana::capture`. + +@copyright Louis Dionne 2013-2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_FUNCTIONAL_CAPTURE_HPP +#define BOOST_HANA_FUNCTIONAL_CAPTURE_HPP + +#include <boost/hana/basic_tuple.hpp> +#include <boost/hana/config.hpp> +#include <boost/hana/detail/decay.hpp> +#include <boost/hana/functional/partial.hpp> + +#include <cstddef> +#include <utility> + + +BOOST_HANA_NAMESPACE_BEGIN + //! @ingroup group-functional + //! Create a function capturing the given variables. + //! + //! Given 0 or more variables, `capture` creates a closure that can be + //! used to partially apply a function. This is very similar to `partial`, + //! except that `capture` allows the partially applied function to be + //! specified later. Specifically, `capture(vars...)` is a function object + //! taking a function `f` and returning `f` partially applied to `vars...`. + //! In other words, + //! @code + //! capture(vars...)(f)(args...) == f(vars..., args...) + //! @endcode + //! + //! @note + //! The arity of `f` must match the total number of arguments passed to + //! it, i.e. `sizeof...(vars) + sizeof...(args)`. + //! + //! + //! Example + //! ------- + //! @include example/functional/capture.cpp +#ifdef BOOST_HANA_DOXYGEN_INVOKED + constexpr auto capture = [](auto&& ...variables) { + return [perfect-capture](auto&& f) { + return [perfect-capture](auto&& ...args) -> decltype(auto) { + return forwarded(f)(forwarded(variables)..., forwarded(args)...); + }; + }; + }; +#else + namespace detail { + template <typename F, typename Closure, std::size_t ...i> + constexpr auto apply_capture(F&& f, Closure&& closure, std::index_sequence<i...>) { + return hana::partial(static_cast<F&&>(f), + hana::get_impl<i>(static_cast<Closure&&>(closure).storage_)... + ); + } + } + + template <typename ...X> + struct capture_t; + + struct make_capture_t { + struct secret { }; + template <typename ...X> + constexpr capture_t<typename detail::decay<X>::type...> + operator()(X&& ...x) const { + return {secret{}, static_cast<X&&>(x)...}; + } + }; + + template <typename ...X> + struct capture_t { + template <typename ...Y> + constexpr capture_t(make_capture_t::secret, Y&& ...y) + : storage_{static_cast<Y&&>(y)...} + { } + + basic_tuple<X...> storage_; + + template <typename F> + constexpr auto operator()(F&& f) const& { + return detail::apply_capture( + static_cast<F&&>(f), *this, + std::make_index_sequence<sizeof...(X)>{} + ); + } + + template <typename F> + constexpr auto operator()(F&& f) & { + return detail::apply_capture( + static_cast<F&&>(f), *this, + std::make_index_sequence<sizeof...(X)>{} + ); + } + + template <typename F> + constexpr auto operator()(F&& f) && { + return detail::apply_capture( + static_cast<F&&>(f), static_cast<capture_t&&>(*this), + std::make_index_sequence<sizeof...(X)>{} + ); + } + }; + + constexpr make_capture_t capture{}; +#endif +BOOST_HANA_NAMESPACE_END + +#endif // !BOOST_HANA_FUNCTIONAL_CAPTURE_HPP diff --git a/boost/hana/functional/compose.hpp b/boost/hana/functional/compose.hpp new file mode 100644 index 0000000000..3250cee4db --- /dev/null +++ b/boost/hana/functional/compose.hpp @@ -0,0 +1,108 @@ +/*! +@file +Defines `boost::hana::compose`. + +@copyright Louis Dionne 2013-2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_FUNCTIONAL_COMPOSE_HPP +#define BOOST_HANA_FUNCTIONAL_COMPOSE_HPP + +#include <boost/hana/config.hpp> +#include <boost/hana/detail/create.hpp> +#include <boost/hana/detail/variadic/foldl1.hpp> + +#include <utility> + + +BOOST_HANA_NAMESPACE_BEGIN + //! @ingroup group-functional + //! Return the composition of two functions or more. + //! + //! `compose` is defined inductively. When given more than two functions, + //! `compose(f, g, h...)` is equivalent to `compose(f, compose(g, h...))`. + //! When given two functions, `compose(f, g)` is a function such that + //! @code + //! compose(f, g)(x, y...) == f(g(x), y...) + //! @endcode + //! + //! If you need composition of the form `f(g(x, y...))`, use `demux` instead. + //! + //! @note + //! `compose` is an associative operation; `compose(f, compose(g, h))` + //! is equivalent to `compose(compose(f, g), h)`. + //! + //! @internal + //! ### Proof of associativity + //! + //! @code + //! compose(f, compose(g, h))(x, xs...) == f(compose(g, h)(x), xs...) + //! == f(g(h(x)), xs...) + //! + //! compose(compose(f, g), h)(x, xs...) == compose(f, g)(h(x), xs...) + //! == f(g(h(x)), xs...) + //! @endcode + //! @endinternal + //! + //! ### Example + //! @include example/functional/compose.cpp +#ifdef BOOST_HANA_DOXYGEN_INVOKED + constexpr auto compose = [](auto&& f1, auto&& f2, ..., auto&& fn) { + return [perfect-capture](auto&& x, auto&& ...xs) -> decltype(auto) { + return forwarded(f1)( + forwarded(f2)( + ... + forwarded(fn)(forwarded(x)) + ), + forwarded(xs)... + ); + } + }; +#else + template <typename F, typename G> + struct _compose { + F f; G g; + + template <typename X, typename ...Xs> + constexpr decltype(auto) operator()(X&& x, Xs&& ...xs) const& { + return f( + g(static_cast<X&&>(x)), + static_cast<Xs&&>(xs)... + ); + } + + template <typename X, typename ...Xs> + constexpr decltype(auto) operator()(X&& x, Xs&& ...xs) & { + return f( + g(static_cast<X&&>(x)), + static_cast<Xs&&>(xs)... + ); + } + + template <typename X, typename ...Xs> + constexpr decltype(auto) operator()(X&& x, Xs&& ...xs) && { + return std::move(f)( + std::move(g)(static_cast<X&&>(x)), + static_cast<Xs&&>(xs)... + ); + } + }; + + struct _make_compose { + template <typename F, typename G, typename ...H> + constexpr decltype(auto) operator()(F&& f, G&& g, H&& ...h) const { + return detail::variadic::foldl1(detail::create<_compose>{}, + static_cast<F&&>(f), + static_cast<G&&>(g), + static_cast<H&&>(h)... + ); + } + }; + + constexpr _make_compose compose{}; +#endif +BOOST_HANA_NAMESPACE_END + +#endif // !BOOST_HANA_FUNCTIONAL_COMPOSE_HPP diff --git a/boost/hana/functional/curry.hpp b/boost/hana/functional/curry.hpp new file mode 100644 index 0000000000..23b3694b8c --- /dev/null +++ b/boost/hana/functional/curry.hpp @@ -0,0 +1,169 @@ +/*! +@file +Defines `boost::hana::curry`. + +@copyright Louis Dionne 2013-2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_FUNCTIONAL_CURRY_HPP +#define BOOST_HANA_FUNCTIONAL_CURRY_HPP + +#include <boost/hana/config.hpp> +#include <boost/hana/functional/apply.hpp> +#include <boost/hana/functional/partial.hpp> + +#include <cstddef> +#include <type_traits> +#include <utility> + + +BOOST_HANA_NAMESPACE_BEGIN + //! @ingroup group-functional + //! Curry a function up to the given number of arguments. + //! + //! [Currying][Wikipedia.currying] is a technique in which we consider a + //! function taking multiple arguments (or, equivalently, a tuple of + //! arguments), and turn it into a function which takes a single argument + //! and returns a function to handle the remaining arguments. To help + //! visualize, let's denote the type of a function `f` which takes + //! arguments of types `X1, ..., Xn` and returns a `R` as + //! @code + //! (X1, ..., Xn) -> R + //! @endcode + //! + //! Then, currying is the process of taking `f` and turning it into an + //! equivalent function (call it `g`) of type + //! @code + //! X1 -> (X2 -> (... -> (Xn -> R))) + //! @endcode + //! + //! This gives us the following equivalence, where `x1`, ..., `xn` are + //! objects of type `X1`, ..., `Xn` respectively: + //! @code + //! f(x1, ..., xn) == g(x1)...(xn) + //! @endcode + //! + //! Currying can be useful in several situations, especially when working + //! with higher-order functions. + //! + //! This `curry` utility is an implementation of currying in C++. + //! Specifically, `curry<n>(f)` is a function such that + //! @code + //! curry<n>(f)(x1)...(xn) == f(x1, ..., xn) + //! @endcode + //! + //! Note that the `n` has to be specified explicitly because the existence + //! of functions with variadic arguments in C++ make it impossible to know + //! when currying should stop. + //! + //! Unlike usual currying, this implementation also allows a curried + //! function to be called with several arguments at a time. Hence, the + //! following always holds + //! @code + //! curry<n>(f)(x1, ..., xk) == curry<n - k>(f)(x1)...(xk) + //! @endcode + //! + //! Of course, this requires `k` to be less than or equal to `n`; failure + //! to satisfy this will trigger a static assertion. This syntax is + //! supported because it makes curried functions usable where normal + //! functions are expected. + //! + //! Another "extension" is that `curry<0>(f)` is supported: `curry<0>(f)` + //! is a nullary function; whereas the classical definition for currying + //! seems to leave this case undefined, as nullary functions don't make + //! much sense in purely functional languages. + //! + //! + //! Example + //! ------- + //! @include example/functional/curry.cpp + //! + //! + //! [Wikipedia.currying]: http://en.wikipedia.org/wiki/Currying +#ifdef BOOST_HANA_DOXYGEN_INVOKED + template <std::size_t n> + constexpr auto curry = [](auto&& f) { + return [perfect-capture](auto&& x1) { + return [perfect-capture](auto&& x2) { + ... + return [perfect-capture](auto&& xn) -> decltype(auto) { + return forwarded(f)( + forwarded(x1), forwarded(x2), ..., forwarded(xn) + ); + }; + }; + }; + }; +#else + template <std::size_t n, typename F> + struct curry_t; + + template <std::size_t n> + struct make_curry_t { + template <typename F> + constexpr curry_t<n, typename std::decay<F>::type> + operator()(F&& f) const { return {static_cast<F&&>(f)}; } + }; + + template <std::size_t n> + constexpr make_curry_t<n> curry{}; + + namespace curry_detail { + template <std::size_t n> + constexpr make_curry_t<n> curry_or_call{}; + + template <> + constexpr auto curry_or_call<0> = apply; + } + + template <std::size_t n, typename F> + struct curry_t { + F f; + + template <typename ...X> + constexpr decltype(auto) operator()(X&& ...x) const& { + static_assert(sizeof...(x) <= n, + "too many arguments provided to boost::hana::curry"); + return curry_detail::curry_or_call<n - sizeof...(x)>( + partial(f, static_cast<X&&>(x)...) + ); + } + + template <typename ...X> + constexpr decltype(auto) operator()(X&& ...x) & { + static_assert(sizeof...(x) <= n, + "too many arguments provided to boost::hana::curry"); + return curry_detail::curry_or_call<n - sizeof...(x)>( + partial(f, static_cast<X&&>(x)...) + ); + } + + template <typename ...X> + constexpr decltype(auto) operator()(X&& ...x) && { + static_assert(sizeof...(x) <= n, + "too many arguments provided to boost::hana::curry"); + return curry_detail::curry_or_call<n - sizeof...(x)>( + partial(std::move(f), static_cast<X&&>(x)...) + ); + } + }; + + template <typename F> + struct curry_t<0, F> { + F f; + + constexpr decltype(auto) operator()() const& + { return f(); } + + constexpr decltype(auto) operator()() & + { return f(); } + + constexpr decltype(auto) operator()() && + { return std::move(f)(); } + }; +#endif +BOOST_HANA_NAMESPACE_END + +#endif // !BOOST_HANA_FUNCTIONAL_CURRY_HPP diff --git a/boost/hana/functional/demux.hpp b/boost/hana/functional/demux.hpp new file mode 100644 index 0000000000..c1ed03634e --- /dev/null +++ b/boost/hana/functional/demux.hpp @@ -0,0 +1,269 @@ +/*! +@file +Defines `boost::hana::demux`. + +@copyright Louis Dionne 2013-2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_FUNCTIONAL_DEMUX_HPP +#define BOOST_HANA_FUNCTIONAL_DEMUX_HPP + +#include <boost/hana/basic_tuple.hpp> +#include <boost/hana/config.hpp> +#include <boost/hana/detail/decay.hpp> + +#include <cstddef> +#include <utility> + + +BOOST_HANA_NAMESPACE_BEGIN + //! @ingroup group-functional + //! Invoke a function with the results of invoking other functions + //! on its arguments. + //! + //! Specifically, `demux(f)(g...)` is a function such that + //! @code + //! demux(f)(g...)(x...) == f(g(x...)...) + //! @endcode + //! + //! Each `g` is called with all the arguments, and then `f` is called + //! with the result of each `g`. Hence, the arity of `f` must match + //! the number of `g`s. + //! + //! This is called `demux` because of a vague similarity between this + //! device and a demultiplexer in signal processing. `demux` takes what + //! can be seen as a continuation (`f`), a bunch of functions to split a + //! signal (`g...`) and zero or more arguments representing the signal + //! (`x...`). Then, it calls the continuation with the result of + //! splitting the signal with whatever functions where given. + //! + //! @note + //! When used with two functions only, `demux` is associative. In other + //! words (and noting `demux(f, g) = demux(f)(g)` to ease the notation), + //! it is true that `demux(demux(f, g), h) == demux(f, demux(g, h))`. + //! + //! + //! Signature + //! --------- + //! The signature of `demux` is + //! \f[ + //! \mathtt{demux} : + //! (B_1 \times \dotsb \times B_n \to C) + //! \to ((A_1 \times \dotsb \times A_n \to B_1) + //! \times \dotsb + //! \times (A_1 \times \dotsb \times A_n \to B_n)) + //! \to (A_1 \times \dotsb \times A_n \to C) + //! \f] + //! + //! This can be rewritten more tersely as + //! \f[ + //! \mathtt{demux} : + //! \left(\prod_{i=1}^n B_i \to C \right) + //! \to \prod_{j=1}^n \left(\prod_{i=1}^n A_i \to B_j \right) + //! \to \left(\prod_{i=1}^n A_i \to C \right) + //! \f] + //! + //! + //! Link with normal composition + //! ---------------------------- + //! The signature of `compose` is + //! \f[ + //! \mathtt{compose} : (B \to C) \times (A \to B) \to (A \to C) + //! \f] + //! + //! A valid observation is that this coincides exactly with the type + //! of `demux` when used with a single unary function. Actually, both + //! functions are equivalent: + //! @code + //! demux(f)(g)(x) == compose(f, g)(x) + //! @endcode + //! + //! However, let's now consider the curried version of `compose`, + //! `curry<2>(compose)`: + //! \f[ + //! \mathtt{curry_2(compose)} : (B \to C) \to ((A \to B) \to (A \to C)) + //! \f] + //! + //! For the rest of this explanation, we'll just consider the curried + //! version of `compose` and so we'll use `compose` instead of + //! `curry<2>(compose)` to lighten the notation. With currying, we can + //! now consider `compose` applied to itself: + //! \f[ + //! \mathtt{compose(compose, compose)} : + //! (B \to C) \to (A_1 \to A_2 \to B) \to (A_1 \to A_2 \to C) + //! \f] + //! + //! If we uncurry deeply the above expression, we obtain + //! \f[ + //! \mathtt{compose(compose, compose)} : + //! (B \to C) \times (A_1 \times A_2 \to B) \to (A_1 \times A_2 \to C) + //! \f] + //! + //! This signature is exactly the same as that of `demux` when given a + //! single binary function, and indeed they are equivalent definitions. + //! We can also generalize this further by considering + //! `compose(compose(compose, compose), compose)`: + //! \f[ + //! \mathtt{compose(compose(compose, compose), compose)} : + //! (B \to C) \to (A_1 \to A_2 \to A_3 \to B) + //! \to (A_1 \to A_2 \to A_3 \to C) + //! \f] + //! + //! which uncurries to + //! \f[ + //! \mathtt{compose(compose(compose, compose), compose)} : + //! (B \to C) \times (A_1 \times A_2 \times A_3 \to B) + //! \to (A_1 \times A_2 \times A_3 \to C) + //! \f] + //! + //! This signature is exactly the same as that of `demux` when given a + //! single ternary function. Hence, for a single n-ary function `g`, + //! `demux(f)(g)` is equivalent to the n-times composition of `compose` + //! with itself, applied to `g` and `f`: + //! @code + //! demux(f)(g) == fold_left([compose, ..., compose], id, compose)(g, f) + //! // ^^^^^^^^^^^^^^^^^^^^^ n times + //! @endcode + //! + //! More information on this insight can be seen [here][1]. Also, I'm + //! not sure how this insight could be generalized to more than one + //! function `g`, or if that is even possible. + //! + //! + //! Proof of associativity in the binary case + //! ----------------------------------------- + //! As explained above, `demux` is associative when it is used with + //! two functions only. Indeed, given functions `f`, `g` and `h` with + //! suitable signatures, we have + //! @code + //! demux(f)(demux(g)(h))(x...) == f(demux(g)(h)(x...)) + //! == f(g(h(x...))) + //! @endcode + //! + //! On the other hand, we have + //! @code + //! demux(demux(f)(g))(h)(x...) == demux(f)(g)(h(x...)) + //! == f(g(h(x...))) + //! @endcode + //! + //! and hence `demux` is associative in the binary case. + //! + //! + //! Example + //! ------- + //! @include example/functional/demux.cpp + //! + //! [1]: http://stackoverflow.com/q/5821089/627587 +#ifdef BOOST_HANA_DOXYGEN_INVOKED + constexpr auto demux = [](auto&& f) { + return [perfect-capture](auto&& ...g) { + return [perfect-capture](auto&& ...x) -> decltype(auto) { + // x... can't be forwarded unless there is a single g + // function, or that could cause double-moves. + return forwarded(f)(forwarded(g)(x...)...); + }; + }; + }; +#else + template <typename F> + struct pre_demux_t; + + struct make_pre_demux_t { + struct secret { }; + template <typename F> + constexpr pre_demux_t<typename detail::decay<F>::type> operator()(F&& f) const { + return {static_cast<F&&>(f)}; + } + }; + + template <typename Indices, typename F, typename ...G> + struct demux_t; + + template <typename F> + struct pre_demux_t { + F f; + + template <typename ...G> + constexpr demux_t<std::make_index_sequence<sizeof...(G)>, F, + typename detail::decay<G>::type...> + operator()(G&& ...g) const& { + return {make_pre_demux_t::secret{}, this->f, static_cast<G&&>(g)...}; + } + + template <typename ...G> + constexpr demux_t<std::make_index_sequence<sizeof...(G)>, F, + typename detail::decay<G>::type...> + operator()(G&& ...g) && { + return {make_pre_demux_t::secret{}, static_cast<F&&>(this->f), static_cast<G&&>(g)...}; + } + }; + + template <std::size_t ...n, typename F, typename ...G> + struct demux_t<std::index_sequence<n...>, F, G...> { + template <typename ...T> + constexpr demux_t(make_pre_demux_t::secret, T&& ...t) + : storage_{static_cast<T&&>(t)...} + { } + + basic_tuple<F, G...> storage_; + + template <typename ...X> + constexpr decltype(auto) operator()(X&& ...x) const& { + return hana::get_impl<0>(storage_)( + hana::get_impl<n+1>(storage_)(x...)... + ); + } + + template <typename ...X> + constexpr decltype(auto) operator()(X&& ...x) & { + return hana::get_impl<0>(storage_)( + hana::get_impl<n+1>(storage_)(x...)... + ); + } + + template <typename ...X> + constexpr decltype(auto) operator()(X&& ...x) && { + return static_cast<F&&>(hana::get_impl<0>(storage_))( + static_cast<G&&>(hana::get_impl<n+1>(storage_))(x...)... + ); + } + }; + + template <typename F, typename G> + struct demux_t<std::index_sequence<0>, F, G> { + template <typename ...T> + constexpr demux_t(make_pre_demux_t::secret, T&& ...t) + : storage_{static_cast<T&&>(t)...} + { } + + basic_tuple<F, G> storage_; + + template <typename ...X> + constexpr decltype(auto) operator()(X&& ...x) const& { + return hana::get_impl<0>(storage_)( + hana::get_impl<1>(storage_)(static_cast<X&&>(x)...) + ); + } + + template <typename ...X> + constexpr decltype(auto) operator()(X&& ...x) & { + return hana::get_impl<0>(storage_)( + hana::get_impl<1>(storage_)(static_cast<X&&>(x)...) + ); + } + + template <typename ...X> + constexpr decltype(auto) operator()(X&& ...x) && { + return static_cast<F&&>(hana::get_impl<0>(storage_))( + static_cast<G&&>(hana::get_impl<1>(storage_))(static_cast<X&&>(x)...) + ); + } + }; + + constexpr make_pre_demux_t demux{}; +#endif +BOOST_HANA_NAMESPACE_END + +#endif // !BOOST_HANA_FUNCTIONAL_DEMUX_HPP diff --git a/boost/hana/functional/fix.hpp b/boost/hana/functional/fix.hpp new file mode 100644 index 0000000000..007ecba1a1 --- /dev/null +++ b/boost/hana/functional/fix.hpp @@ -0,0 +1,83 @@ +/*! +@file +Defines `boost::hana::fix`. + +@copyright Louis Dionne 2013-2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_FUNCTIONAL_FIX_HPP +#define BOOST_HANA_FUNCTIONAL_FIX_HPP + +#include <boost/hana/config.hpp> +#include <boost/hana/detail/create.hpp> + +#include <utility> + + +BOOST_HANA_NAMESPACE_BEGIN + //! @ingroup group-functional + //! Return a function computing the fixed point of a function. + //! + //! `fix` is an implementation of the [Y-combinator][], also called the + //! fixed-point combinator. It encodes the idea of recursion, and in fact + //! any recursive function can be written in terms of it. + //! + //! Specifically, `fix(f)` is a function such that + //! @code + //! fix(f)(x...) == f(fix(f), x...) + //! @endcode + //! + //! This definition allows `f` to use its first argument as a continuation + //! to call itself recursively. Indeed, if `f` calls its first argument + //! with `y...`, it is equivalent to calling `f(fix(f), y...)` per the + //! above equation. + //! + //! Most of the time, it is more convenient and efficient to define + //! recursive functions without using a fixed-point combinator. However, + //! there are some cases where `fix` provides either more flexibility + //! (e.g. the ability to change the callback inside `f`) or makes it + //! possible to write functions that couldn't be defined recursively + //! otherwise. + //! + //! @param f + //! A function called as `f(self, x...)`, where `x...` are the arguments + //! in the `fix(f)(x...)` expression and `self` is `fix(f)`. + //! + //! ### Example + //! @include example/functional/fix.cpp + //! + //! [Y-combinator]: http://en.wikipedia.org/wiki/Fixed-point_combinator +#ifdef BOOST_HANA_DOXYGEN_INVOKED + constexpr auto fix = [](auto&& f) { + return [perfect-capture](auto&& ...x) -> decltype(auto) { + return forwarded(f)(fix(f), forwarded(x)...); + }; + }; +#else + template <typename F> + struct fix_t; + + constexpr detail::create<fix_t> fix{}; + + template <typename F> + struct fix_t { + F f; + + template <typename ...X> + constexpr decltype(auto) operator()(X&& ...x) const& + { return f(fix(f), static_cast<X&&>(x)...); } + + template <typename ...X> + constexpr decltype(auto) operator()(X&& ...x) & + { return f(fix(f), static_cast<X&&>(x)...); } + + template <typename ...X> + constexpr decltype(auto) operator()(X&& ...x) && + { return std::move(f)(fix(f), static_cast<X&&>(x)...); } + }; +#endif +BOOST_HANA_NAMESPACE_END + +#endif // !BOOST_HANA_FUNCTIONAL_FIX_HPP diff --git a/boost/hana/functional/flip.hpp b/boost/hana/functional/flip.hpp new file mode 100644 index 0000000000..3da81eddd7 --- /dev/null +++ b/boost/hana/functional/flip.hpp @@ -0,0 +1,73 @@ +/*! +@file +Defines `boost::hana::flip`. + +@copyright Louis Dionne 2013-2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_FUNCTIONAL_FLIP_HPP +#define BOOST_HANA_FUNCTIONAL_FLIP_HPP + +#include <boost/hana/config.hpp> +#include <boost/hana/detail/create.hpp> + +#include <utility> + + +BOOST_HANA_NAMESPACE_BEGIN + //! @ingroup group-functional + //! Invoke a function with its two first arguments reversed. + //! + //! Specifically, `flip(f)` is a function such that + //! @code + //! flip(f)(x, y, z...) == f(y, x, z...) + //! @endcode + //! + //! ### Example + //! @include example/functional/flip.cpp +#ifdef BOOST_HANA_DOXYGEN_INVOKED + constexpr auto flip = [](auto&& f) { + return [perfect-capture](auto&& x, auto&& y, auto&& ...z) -> decltype(auto) { + return forwarded(f)(forwarded(y), forwarded(x), forwarded(z)...); + }; + }; +#else + template <typename F> + struct flip_t { + F f; + + template <typename X, typename Y, typename ...Z> + constexpr decltype(auto) operator()(X&& x, Y&& y, Z&& ...z) const& { + return f( + static_cast<Y&&>(y), + static_cast<X&&>(x), + static_cast<Z&&>(z)... + ); + } + + template <typename X, typename Y, typename ...Z> + constexpr decltype(auto) operator()(X&& x, Y&& y, Z&& ...z) & { + return f( + static_cast<Y&&>(y), + static_cast<X&&>(x), + static_cast<Z&&>(z)... + ); + } + + template <typename X, typename Y, typename ...Z> + constexpr decltype(auto) operator()(X&& x, Y&& y, Z&& ...z) && { + return std::move(f)( + static_cast<Y&&>(y), + static_cast<X&&>(x), + static_cast<Z&&>(z)... + ); + } + }; + + constexpr detail::create<flip_t> flip{}; +#endif +BOOST_HANA_NAMESPACE_END + +#endif // !BOOST_HANA_FUNCTIONAL_FLIP_HPP diff --git a/boost/hana/functional/id.hpp b/boost/hana/functional/id.hpp new file mode 100644 index 0000000000..e6b4e32b79 --- /dev/null +++ b/boost/hana/functional/id.hpp @@ -0,0 +1,38 @@ +/*! +@file +Defines `boost::hana::id`. + +@copyright Louis Dionne 2013-2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_FUNCTIONAL_ID_HPP +#define BOOST_HANA_FUNCTIONAL_ID_HPP + +#include <boost/hana/config.hpp> + + +BOOST_HANA_NAMESPACE_BEGIN + //! @ingroup group-functional + //! The identity function -- returns its argument unchanged. + //! + //! ### Example + //! @include example/functional/id.cpp +#ifdef BOOST_HANA_DOXYGEN_INVOKED + constexpr auto id = [](auto&& x) -> decltype(auto) { + return forwarded(x); + }; +#else + struct id_t { + template <typename T> + constexpr T operator()(T&& t) const { + return static_cast<T&&>(t); + } + }; + + constexpr id_t id{}; +#endif +BOOST_HANA_NAMESPACE_END + +#endif // !BOOST_HANA_FUNCTIONAL_ID_HPP diff --git a/boost/hana/functional/infix.hpp b/boost/hana/functional/infix.hpp new file mode 100644 index 0000000000..471e4f3ac9 --- /dev/null +++ b/boost/hana/functional/infix.hpp @@ -0,0 +1,184 @@ +/*! +@file +Defines `boost::hana::infix`. + +@copyright Louis Dionne 2013-2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_FUNCTIONAL_INFIX_HPP +#define BOOST_HANA_FUNCTIONAL_INFIX_HPP + +#include <boost/hana/config.hpp> +#include <boost/hana/functional/partial.hpp> +#include <boost/hana/functional/reverse_partial.hpp> + +#include <type_traits> +#include <utility> + + +BOOST_HANA_NAMESPACE_BEGIN + //! @ingroup group-functional + //! Return an equivalent function that can also be applied in infix + //! notation. + //! + //! Specifically, `infix(f)` is an object such that: + //! @code + //! infix(f)(x1, ..., xn) == f(x1, ..., xn) + //! x ^infix(f)^ y == f(x, y) + //! @endcode + //! + //! Hence, the returned function can still be applied using the usual + //! function call syntax, but it also gains the ability to be applied in + //! infix notation. The infix syntax allows a great deal of expressiveness, + //! especially when used in combination with some higher order algorithms. + //! Since `operator^` is left-associative, `x ^infix(f)^ y` is actually + //! parsed as `(x ^infix(f))^ y`. However, for flexibility, the order in + //! which both arguments are applied in infix notation does not matter. + //! Hence, it is always the case that + //! @code + //! (x ^ infix(f)) ^ y == x ^ (infix(f) ^ y) + //! @endcode + //! + //! However, note that applying more than one argument in infix + //! notation to the same side of the operator will result in a + //! compile-time assertion: + //! @code + //! (infix(f) ^ x) ^ y; // compile-time assertion + //! y ^ (x ^ infix(f)); // compile-time assertion + //! @endcode + //! + //! Additionally, a function created with `infix` may be partially applied + //! in infix notation. Specifically, + //! @code + //! (x ^ infix(f))(y1, ..., yn) == f(x, y1, ..., yn) + //! (infix(f) ^ y)(x1, ..., xn) == f(x1, ..., xn, y) + //! @endcode + //! + //! @internal + //! ### Rationales + //! 1. The `^` operator was chosen because it is left-associative and + //! has a low enough priority so that most expressions will render + //! the expected behavior. + //! 2. The operator can't be customimzed because that would require more + //! sophistication in the implementation; I want to keep it as simple + //! as possible. There is also an advantage in having a uniform syntax + //! for infix application. + //! @endinternal + //! + //! @param f + //! The function which gains the ability to be applied in infix notation. + //! The function must be at least binary; a compile-time error will be + //! triggered otherwise. + //! + //! ### Example + //! @include example/functional/infix.cpp +#ifdef BOOST_HANA_DOXYGEN_INVOKED + constexpr auto infix = [](auto f) { + return unspecified; + }; +#else + namespace infix_detail { + // This needs to be in the same namespace as `operator^` so it can be + // found by ADL. + template <bool left, bool right, typename F> + struct infix_t { + F f; + + template <typename ...X> + constexpr decltype(auto) operator()(X&& ...x) const& + { return f(static_cast<X&&>(x)...); } + + template <typename ...X> + constexpr decltype(auto) operator()(X&& ...x) & + { return f(static_cast<X&&>(x)...); } + + template <typename ...X> + constexpr decltype(auto) operator()(X&& ...x) && + { return std::move(f)(static_cast<X&&>(x)...); } + }; + + template <bool left, bool right> + struct make_infix { + template <typename F> + constexpr infix_t<left, right, typename std::decay<F>::type> + operator()(F&& f) const { return {static_cast<F&&>(f)}; } + }; + + template <bool left, bool right> + struct Infix; + struct Object; + + template <typename T> + struct dispatch { using type = Object; }; + + template <bool left, bool right, typename F> + struct dispatch<infix_t<left, right, F>> { + using type = Infix<left, right>; + }; + + template <typename, typename> + struct bind_infix; + + // infix(f) ^ y + template <> + struct bind_infix<Infix<false, false>, Object> { + template <typename F, typename Y> + static constexpr decltype(auto) apply(F&& f, Y&& y) { + return make_infix<false, true>{}( + hana::reverse_partial( + static_cast<F&&>(f), static_cast<Y&&>(y) + ) + ); + } + }; + + // (x^infix(f)) ^ y + template <> + struct bind_infix<Infix<true, false>, Object> { + template <typename F, typename Y> + static constexpr decltype(auto) apply(F&& f, Y&& y) { + return static_cast<F&&>(f)(static_cast<Y&&>(y)); + } + }; + + // x ^ infix(f) + template <> + struct bind_infix<Object, Infix<false, false>> { + template <typename X, typename F> + static constexpr decltype(auto) apply(X&& x, F&& f) { + return make_infix<true, false>{}( + hana::partial(static_cast<F&&>(f), static_cast<X&&>(x)) + ); + } + }; + + // x ^ (infix(f)^y) + template <> + struct bind_infix<Object, Infix<false, true>> { + template <typename X, typename F> + static constexpr decltype(auto) apply(X&& x, F&& f) { + return static_cast<F&&>(f)(static_cast<X&&>(x)); + } + }; + + template <typename T> + using strip = typename std::remove_cv< + typename std::remove_reference<T>::type + >::type; + + template <typename X, typename Y> + constexpr decltype(auto) operator^(X&& x, Y&& y) { + return bind_infix< + typename dispatch<strip<X>>::type, + typename dispatch<strip<Y>>::type + >::apply(static_cast<X&&>(x), static_cast<Y&&>(y)); + } + } // end namespace infix_detail + + constexpr infix_detail::make_infix<false, false> infix{}; +#endif +BOOST_HANA_NAMESPACE_END + +#endif // !BOOST_HANA_FUNCTIONAL_INFIX_HPP diff --git a/boost/hana/functional/iterate.hpp b/boost/hana/functional/iterate.hpp new file mode 100644 index 0000000000..b2ec27f5ec --- /dev/null +++ b/boost/hana/functional/iterate.hpp @@ -0,0 +1,201 @@ +/*! +@file +Defines `boost::hana::iterate`. + +@copyright Louis Dionne 2013-2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_FUNCTIONAL_ITERATE_HPP +#define BOOST_HANA_FUNCTIONAL_ITERATE_HPP + +#include <boost/hana/config.hpp> +#include <boost/hana/core/when.hpp> +#include <boost/hana/functional/partial.hpp> + +#include <cstddef> + + +BOOST_HANA_NAMESPACE_BEGIN + //! @ingroup group-functional + //! Applies another function `n` times to its argument. + //! + //! Given a function `f` and an argument `x`, `iterate<n>(f, x)` returns + //! the result of applying `f` `n` times to its argument. In other words, + //! @code + //! iterate<n>(f, x) == f(f( ... f(x))) + //! ^^^^^^^^^^ n times total + //! @endcode + //! + //! If `n == 0`, `iterate<n>(f, x)` returns the `x` argument unchanged + //! and `f` is never applied. It is important to note that the function + //! passed to `iterate<n>` must be a unary function. Indeed, since `f` + //! will be called with the result of the previous `f` application, it + //! may only take a single argument. + //! + //! In addition to what's documented above, `iterate` can also be + //! partially applied to the function argument out-of-the-box. In + //! other words, `iterate<n>(f)` is a function object applying `f` + //! `n` times to the argument it is called with, which means that + //! @code + //! iterate<n>(f)(x) == iterate<n>(f, x) + //! @endcode + //! + //! This is provided for convenience, and it turns out to be especially + //! useful in conjunction with higher-order algorithms. + //! + //! + //! Signature + //! --------- + //! Given a function \f$ f : T \to T \f$ and `x` and argument of data + //! type `T`, the signature is + //! \f$ + //! \mathtt{iterate_n} : (T \to T) \times T \to T + //! \f$ + //! + //! @tparam n + //! An unsigned integer representing the number of times that `f` + //! should be applied to its argument. + //! + //! @param f + //! A function to apply `n` times to its argument. + //! + //! @param x + //! The initial value to call `f` with. + //! + //! + //! Example + //! ------- + //! @include example/functional/iterate.cpp +#ifdef BOOST_HANA_DOXYGEN_INVOKED + template <std::size_t n> + constexpr auto iterate = [](auto&& f) { + return [perfect-capture](auto&& x) -> decltype(auto) { + return f(f( ... f(forwarded(x)))); + }; + }; +#else + template <std::size_t n, typename = when<true>> + struct iterate_t; + + template <> + struct iterate_t<0> { + template <typename F, typename X> + constexpr X operator()(F&&, X&& x) const + { return static_cast<X&&>(x); } + }; + + template <> + struct iterate_t<1> { + template <typename F, typename X> + constexpr decltype(auto) operator()(F&& f, X&& x) const { + return f(static_cast<X&&>(x)); + } + }; + + template <> + struct iterate_t<2> { + template <typename F, typename X> + constexpr decltype(auto) operator()(F&& f, X&& x) const { + return f(f(static_cast<X&&>(x))); + } + }; + + template <> + struct iterate_t<3> { + template <typename F, typename X> + constexpr decltype(auto) operator()(F&& f, X&& x) const { + return f(f(f(static_cast<X&&>(x)))); + } + }; + + template <> + struct iterate_t<4> { + template <typename F, typename X> + constexpr decltype(auto) operator()(F&& f, X&& x) const { + return f(f(f(f(static_cast<X&&>(x))))); + } + }; + + template <> + struct iterate_t<5> { + template <typename F, typename X> + constexpr decltype(auto) operator()(F&& f, X&& x) const { + return f(f(f(f(f(static_cast<X&&>(x)))))); + } + }; + + template <std::size_t n> + struct iterate_t<n, when<(n >= 6) && (n < 12)>> { + template <typename F, typename X> + constexpr decltype(auto) operator()(F&& f, X&& x) const { + return iterate_t<n - 6>{}(f, + f(f(f(f(f(f(static_cast<X&&>(x))))))) + ); + } + }; + + template <std::size_t n> + struct iterate_t<n, when<(n >= 12) && (n < 24)>> { + template <typename F, typename X> + constexpr decltype(auto) operator()(F&& f, X&& x) const { + return iterate_t<n - 12>{}(f, + f(f(f(f(f(f(f(f(f(f(f(f( + static_cast<X&&>(x) + )))))))))))) + ); + } + }; + + template <std::size_t n> + struct iterate_t<n, when<(n >= 24) && (n < 48)>> { + template <typename F, typename X> + constexpr decltype(auto) operator()(F&& f, X&& x) const { + return iterate_t<n - 24>{}(f, + f(f(f(f(f(f(f(f(f(f(f(f( + f(f(f(f(f(f(f(f(f(f(f(f( + static_cast<X&&>(x) + )))))))))))) + )))))))))))) + ); + } + }; + + template <std::size_t n> + struct iterate_t<n, when<(n >= 48)>> { + template <typename F, typename X> + constexpr decltype(auto) operator()(F&& f, X&& x) const { + return iterate_t<n - 48>{}(f, + f(f(f(f(f(f(f(f(f(f(f(f( + f(f(f(f(f(f(f(f(f(f(f(f( + f(f(f(f(f(f(f(f(f(f(f(f( + f(f(f(f(f(f(f(f(f(f(f(f( + static_cast<X&&>(x) + )))))))))))) + )))))))))))) + )))))))))))) + )))))))))))) + ); + } + }; + + template <std::size_t n> + struct make_iterate_t { + template <typename F> + constexpr decltype(auto) operator()(F&& f) const + { return hana::partial(iterate_t<n>{}, static_cast<F&&>(f)); } + + template <typename F, typename X> + constexpr decltype(auto) operator()(F&& f, X&& x) const { + return iterate_t<n>{}(static_cast<F&&>(f), + static_cast<X&&>(x)); + } + }; + + template <std::size_t n> + constexpr make_iterate_t<n> iterate{}; +#endif +BOOST_HANA_NAMESPACE_END + +#endif // !BOOST_HANA_FUNCTIONAL_ITERATE_HPP diff --git a/boost/hana/functional/lockstep.hpp b/boost/hana/functional/lockstep.hpp new file mode 100644 index 0000000000..fe4a12c7bd --- /dev/null +++ b/boost/hana/functional/lockstep.hpp @@ -0,0 +1,114 @@ +/*! +@file +Defines `boost::hana::lockstep`. + +@copyright Louis Dionne 2013-2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_FUNCTIONAL_LOCKSTEP_HPP +#define BOOST_HANA_FUNCTIONAL_LOCKSTEP_HPP + +#include <boost/hana/basic_tuple.hpp> +#include <boost/hana/config.hpp> + +#include <cstddef> +#include <type_traits> +#include <utility> + + +BOOST_HANA_NAMESPACE_BEGIN + //! @ingroup group-functional + //! Invoke a function with the result of invoking other functions on its + //! arguments, in lockstep. + //! + //! Specifically, `lockstep(f)(g1, ..., gN)` is a function such that + //! @code + //! lockstep(f)(g1, ..., gN)(x1, ..., xN) == f(g1(x1), ..., gN(xN)) + //! @endcode + //! + //! Since each `g` is invoked on its corresponding argument in lockstep, + //! the number of arguments must match the number of `g`s. + //! + //! + //! Example + //! ------- + //! @include example/functional/lockstep.cpp +#ifdef BOOST_HANA_DOXYGEN_INVOKED + constexpr auto lockstep = [](auto&& f, auto&& ...g) { + return [perfect-capture](auto&& ...x) -> decltype(auto) { + return forwarded(f)(forwarded(g)(forwarded(x))...); + }; + }; +#else + template <typename Indices, typename F, typename ...G> + struct lockstep_t; + + template <typename F> + struct pre_lockstep_t; + + struct make_pre_lockstep_t { + struct secret { }; + template <typename F> + constexpr pre_lockstep_t<typename std::decay<F>::type> operator()(F&& f) const { + return {static_cast<F&&>(f)}; + } + }; + + template <std::size_t ...n, typename F, typename ...G> + struct lockstep_t<std::index_sequence<n...>, F, G...> { + template <typename ...T> + constexpr lockstep_t(make_pre_lockstep_t::secret, T&& ...t) + : storage_{static_cast<T&&>(t)...} + { } + + basic_tuple<F, G...> storage_; + + template <typename ...X> + constexpr decltype(auto) operator()(X&& ...x) const& { + return hana::get_impl<0>(storage_)( + hana::get_impl<n+1>(storage_)(static_cast<X&&>(x))... + ); + } + + template <typename ...X> + constexpr decltype(auto) operator()(X&& ...x) & { + return hana::get_impl<0>(storage_)( + hana::get_impl<n+1>(storage_)(static_cast<X&&>(x))... + ); + } + + template <typename ...X> + constexpr decltype(auto) operator()(X&& ...x) && { + return static_cast<F&&>(hana::get_impl<0>(storage_))( + static_cast<G&&>(hana::get_impl<n+1>(storage_))(static_cast<X&&>(x))... + ); + } + }; + + template <typename F> + struct pre_lockstep_t { + F f; + + template <typename ...G> + constexpr lockstep_t<std::make_index_sequence<sizeof...(G)>, F, + typename std::decay<G>::type...> + operator()(G&& ...g) const& { + return {make_pre_lockstep_t::secret{}, this->f, static_cast<G&&>(g)...}; + } + + template <typename ...G> + constexpr lockstep_t<std::make_index_sequence<sizeof...(G)>, F, + typename std::decay<G>::type...> + operator()(G&& ...g) && { + return {make_pre_lockstep_t::secret{}, static_cast<F&&>(this->f), + static_cast<G&&>(g)...}; + } + }; + + constexpr make_pre_lockstep_t lockstep{}; +#endif +BOOST_HANA_NAMESPACE_END + +#endif // !BOOST_HANA_FUNCTIONAL_LOCKSTEP_HPP diff --git a/boost/hana/functional/on.hpp b/boost/hana/functional/on.hpp new file mode 100644 index 0000000000..ff40c0db5c --- /dev/null +++ b/boost/hana/functional/on.hpp @@ -0,0 +1,83 @@ +/*! +@file +Defines `boost::hana::on`. + +@copyright Louis Dionne 2013-2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_FUNCTIONAL_ON_HPP +#define BOOST_HANA_FUNCTIONAL_ON_HPP + +#include <boost/hana/config.hpp> +#include <boost/hana/detail/create.hpp> +#include <boost/hana/functional/infix.hpp> + +#include <utility> + + +BOOST_HANA_NAMESPACE_BEGIN + //! @ingroup group-functional + //! Invoke a function with the result of invoking another function on + //! each argument. + //! + //! Specifically, `on(f, g)` is a function such that + //! @code + //! on(f, g)(x...) == f(g(x)...) + //! @endcode + //! + //! For convenience, `on` also supports infix application as provided + //! by `infix`. + //! + //! + //! @note + //! `on` is associative, i.e. `on(f, on(g, h))` is equivalent to + //! `on(on(f, g), h)`. + //! + //! @internal + //! ### Proof of associativity + //! + //! @code + //! on(f, on(g, h))(xs...) == f(on(g, h)(xs)...) + //! == f(g(h(xs))...) + //! + //! on(on(f, g), h)(xs...) == on(f, g)(h(xs)...) + //! == f(g(h(xs))...) + //! @endcode + //! @endinternal + //! + //! + //! ### Example + //! @include example/functional/on.cpp +#ifdef BOOST_HANA_DOXYGEN_INVOKED + constexpr auto on = infix([](auto&& f, auto&& g) { + return [perfect-capture](auto&& ...x) -> decltype(auto) { + return forwarded(f)(g(forwarded(x))...); + }; + }); +#else + template <typename F, typename G> + struct on_t { + F f; G g; + template <typename ...X> + constexpr decltype(auto) operator()(X&& ...x) const& { + return f(g(static_cast<X&&>(x))...); + } + + template <typename ...X> + constexpr decltype(auto) operator()(X&& ...x) & { + return f(g(static_cast<X&&>(x))...); + } + + template <typename ...X> + constexpr decltype(auto) operator()(X&& ...x) && { + return std::move(f)(g(static_cast<X&&>(x))...); + } + }; + + constexpr auto on = infix(detail::create<on_t>{}); +#endif +BOOST_HANA_NAMESPACE_END + +#endif // !BOOST_HANA_FUNCTIONAL_ON_HPP diff --git a/boost/hana/functional/overload.hpp b/boost/hana/functional/overload.hpp new file mode 100644 index 0000000000..b16c87c915 --- /dev/null +++ b/boost/hana/functional/overload.hpp @@ -0,0 +1,88 @@ +/*! +@file +Defines `boost::hana::overload`. + +@copyright Louis Dionne 2013-2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_FUNCTIONAL_OVERLOAD_HPP +#define BOOST_HANA_FUNCTIONAL_OVERLOAD_HPP + +#include <boost/hana/config.hpp> +#include <boost/hana/detail/decay.hpp> + + +BOOST_HANA_NAMESPACE_BEGIN + //! @ingroup group-functional + //! Pick one of several functions to call based on overload resolution. + //! + //! Specifically, `overload(f1, f2, ..., fn)` is a function object such + //! that + //! @code + //! overload(f1, f2, ..., fn)(x...) == fk(x...) + //! @endcode + //! + //! where `fk` is the function of `f1, ..., fn` that would be called if + //! overload resolution was performed amongst that set of functions only. + //! If more than one function `fk` would be picked by overload resolution, + //! then the call is ambiguous. + //! + //! ### Example + //! @include example/functional/overload.cpp +#ifdef BOOST_HANA_DOXYGEN_INVOKED + constexpr auto overload = [](auto&& f1, auto&& f2, ..., auto&& fn) { + return [perfect-capture](auto&& ...x) -> decltype(auto) { + return forwarded(fk)(forwarded(x)...); + }; + }; +#else + template <typename F, typename ...G> + struct overload_t + : overload_t<F>::type + , overload_t<G...>::type + { + using type = overload_t; + using overload_t<F>::type::operator(); + using overload_t<G...>::type::operator(); + + template <typename F_, typename ...G_> + constexpr explicit overload_t(F_&& f, G_&& ...g) + : overload_t<F>::type(static_cast<F_&&>(f)) + , overload_t<G...>::type(static_cast<G_&&>(g)...) + { } + }; + + template <typename F> + struct overload_t<F> { using type = F; }; + + template <typename R, typename ...Args> + struct overload_t<R(*)(Args...)> { + using type = overload_t; + R (*fptr_)(Args...); + + explicit constexpr overload_t(R (*fp)(Args...)) + : fptr_(fp) + { } + + constexpr R operator()(Args ...args) const + { return fptr_(static_cast<Args&&>(args)...); } + }; + + struct make_overload_t { + template <typename ...F, + typename Overload = typename overload_t< + typename detail::decay<F>::type... + >::type + > + constexpr Overload operator()(F&& ...f) const { + return Overload(static_cast<F&&>(f)...); + } + }; + + constexpr make_overload_t overload{}; +#endif +BOOST_HANA_NAMESPACE_END + +#endif // !BOOST_HANA_FUNCTIONAL_OVERLOAD_HPP diff --git a/boost/hana/functional/overload_linearly.hpp b/boost/hana/functional/overload_linearly.hpp new file mode 100644 index 0000000000..3695bf2be2 --- /dev/null +++ b/boost/hana/functional/overload_linearly.hpp @@ -0,0 +1,105 @@ +/*! +@file +Defines `boost::hana::overload_linearly`. + +@copyright Louis Dionne 2013-2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_FUNCTIONAL_OVERLOAD_LINEARLY_HPP +#define BOOST_HANA_FUNCTIONAL_OVERLOAD_LINEARLY_HPP + +#include <boost/hana/config.hpp> + +#include <type_traits> +#include <utility> + + +BOOST_HANA_NAMESPACE_BEGIN + //! @ingroup group-functional + //! Call the first function that produces a valid call expression. + //! + //! Given functions `f1, ..., fn`, `overload_linearly(f1, ..., fn)` is + //! a new function that calls the first `fk` producing a valid call + //! expression with the given arguments. Specifically, + //! @code + //! overload_linearly(f1, ..., fn)(args...) == fk(args...) + //! @endcode + //! + //! where `fk` is the _first_ function such that `fk(args...)` is a valid + //! expression. + //! + //! + //! Example + //! ------- + //! @include example/functional/overload_linearly.cpp +#ifdef BOOST_HANA_DOXYGEN_INVOKED + constexpr auto overload_linearly = [](auto&& f1, auto&& f2, ..., auto&& fn) { + return [perfect-capture](auto&& ...x) -> decltype(auto) { + return forwarded(fk)(forwarded(x)...); + }; + }; +#else + template <typename F, typename G> + struct overload_linearly_t { + F f; + G g; + + private: + template <typename ...Args, typename = + decltype(std::declval<F const&>()(std::declval<Args>()...))> + constexpr F const& which(int) const& { return f; } + + template <typename ...Args, typename = + decltype(std::declval<F&>()(std::declval<Args>()...))> + constexpr F& which(int) & { return f; } + + template <typename ...Args, typename = + decltype(std::declval<F&&>()(std::declval<Args>()...))> + constexpr F which(int) && { return static_cast<F&&>(f); } + + template <typename ...Args> + constexpr G const& which(long) const& { return g; } + + template <typename ...Args> + constexpr G& which(long) & { return g; } + + template <typename ...Args> + constexpr G which(long) && { return static_cast<G&&>(g); } + + public: + template <typename ...Args> + constexpr decltype(auto) operator()(Args&& ...args) const& + { return which<Args...>(int{})(static_cast<Args&&>(args)...); } + + template <typename ...Args> + constexpr decltype(auto) operator()(Args&& ...args) & + { return which<Args...>(int{})(static_cast<Args&&>(args)...); } + + template <typename ...Args> + constexpr decltype(auto) operator()(Args&& ...args) && + { return which<Args...>(int{})(static_cast<Args&&>(args)...); } + }; + + struct make_overload_linearly_t { + template <typename F, typename G> + constexpr overload_linearly_t< + typename std::decay<F>::type, + typename std::decay<G>::type + > operator()(F&& f, G&& g) const { + return {static_cast<F&&>(f), static_cast<G&&>(g)}; + } + + template <typename F, typename G, typename ...H> + constexpr decltype(auto) operator()(F&& f, G&& g, H&& ...h) const { + return (*this)(static_cast<F&&>(f), + (*this)(static_cast<G&&>(g), static_cast<H&&>(h)...)); + } + }; + + constexpr make_overload_linearly_t overload_linearly{}; +#endif +BOOST_HANA_NAMESPACE_END + +#endif // !BOOST_HANA_FUNCTIONAL_OVERLOAD_LINEARLY_HPP diff --git a/boost/hana/functional/partial.hpp b/boost/hana/functional/partial.hpp new file mode 100644 index 0000000000..c84c365bf9 --- /dev/null +++ b/boost/hana/functional/partial.hpp @@ -0,0 +1,107 @@ +/*! +@file +Defines `boost::hana::partial`. + +@copyright Louis Dionne 2013-2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_FUNCTIONAL_PARTIAL_HPP +#define BOOST_HANA_FUNCTIONAL_PARTIAL_HPP + +#include <boost/hana/basic_tuple.hpp> +#include <boost/hana/config.hpp> +#include <boost/hana/detail/decay.hpp> + +#include <cstddef> +#include <utility> + + +BOOST_HANA_NAMESPACE_BEGIN + //! @ingroup group-functional + //! Partially apply a function to some arguments. + //! + //! Given a function `f` and some arguments, `partial` returns a new + //! function corresponding to the partially applied function `f`. This + //! allows providing some arguments to a function and letting the rest + //! of the arguments be provided later. Specifically, `partial(f, x...)` + //! is a function such that + //! @code + //! partial(f, x...)(y...) == f(x..., y...) + //! @endcode + //! + //! @note + //! The arity of `f` must match the total number of arguments passed to + //! it, i.e. `sizeof...(x) + sizeof...(y)`. + //! + //! + //! Example + //! ------- + //! @include example/functional/partial.cpp +#ifdef BOOST_HANA_DOXYGEN_INVOKED + constexpr auto partial = [](auto&& f, auto&& ...x) { + return [perfect-capture](auto&& ...y) -> decltype(auto) { + return forwarded(f)(forwarded(x)..., forwarded(y)...); + }; + }; +#else + template <typename Indices, typename F, typename ...X> + struct partial_t; + + struct make_partial_t { + struct secret { }; + template <typename F, typename ...X> + constexpr partial_t< + std::make_index_sequence<sizeof...(X)>, + typename detail::decay<F>::type, + typename detail::decay<X>::type... + > + operator()(F&& f, X&& ...x) const { + return {secret{}, static_cast<F&&>(f), static_cast<X&&>(x)...}; + } + }; + + template <std::size_t ...n, typename F, typename ...X> + struct partial_t<std::index_sequence<n...>, F, X...> { + // Not needed in theory; workaround for a bug in libstdc++'s tuple, + // which instantiates the default constructor of elements too eagerly. + partial_t() = default; + + template <typename ...T> + constexpr partial_t(make_partial_t::secret, T&& ...t) + : storage_{static_cast<T&&>(t)...} + { } + + basic_tuple<F, X...> storage_; + + template <typename ...Y> + constexpr decltype(auto) operator()(Y&& ...y) const& { + return hana::get_impl<0>(storage_)( + hana::get_impl<n+1>(storage_)..., + static_cast<Y&&>(y)... + ); + } + + template <typename ...Y> + constexpr decltype(auto) operator()(Y&& ...y) & { + return hana::get_impl<0>(storage_)( + hana::get_impl<n+1>(storage_)..., + static_cast<Y&&>(y)... + ); + } + + template <typename ...Y> + constexpr decltype(auto) operator()(Y&& ...y) && { + return static_cast<F&&>(hana::get_impl<0>(storage_))( + static_cast<X&&>(hana::get_impl<n+1>(storage_))..., + static_cast<Y&&>(y)... + ); + } + }; + + constexpr make_partial_t partial{}; +#endif +BOOST_HANA_NAMESPACE_END + +#endif // !BOOST_HANA_FUNCTIONAL_PARTIAL_HPP diff --git a/boost/hana/functional/placeholder.hpp b/boost/hana/functional/placeholder.hpp new file mode 100644 index 0000000000..71a25389cd --- /dev/null +++ b/boost/hana/functional/placeholder.hpp @@ -0,0 +1,263 @@ +/*! +@file +Defines `boost::hana::_`. + +@copyright Louis Dionne 2013-2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_FUNCTIONAL_PLACEHOLDER_HPP +#define BOOST_HANA_FUNCTIONAL_PLACEHOLDER_HPP + +#include <boost/hana/basic_tuple.hpp> +#include <boost/hana/config.hpp> +#include <boost/hana/detail/create.hpp> +#include <boost/hana/detail/decay.hpp> + +#include <cstddef> +#include <utility> + + +BOOST_HANA_NAMESPACE_BEGIN + //! @ingroup group-functional + //! Create simple functions representing C++ operators inline. + //! + //! Specifically, `_` is an object used as a placeholder to build + //! function objects representing calls to C++ operators. It works + //! by overloading the operators between `_` and any object so that + //! they return a function object which actually calls the corresponding + //! operator on its argument(s). Hence, for any supported operator `@`: + //! @code + //! (_ @ _)(x, y) == x @ y + //! @endcode + //! + //! Operators may also be partially applied to one argument inline: + //! @code + //! (x @ _)(y) == x @ y + //! (_ @ y)(x) == x @ y + //! @endcode + //! + //! When invoked with more arguments than required, functions created with + //! `_` will discard the superfluous instead of triggering an error: + //! @code + //! (_ @ _)(x, y, z...) == x @ y + //! @endcode + //! + //! This makes functions created with `_` easier to use in higher-order + //! algorithms, which sometime provide more information than necessary + //! to their callbacks. + //! + //! ### Supported operators + //! - Arithmetic: binary `+`, binary `-`, `/`, `*`, `%`, unary `+`, unary `-` + //! - Bitwise: `~`, `&`, `|`, `^`, `<<`, `>>` + //! - Comparison: `==`, `!=`, `<`, `<=`, `>`, `>=` + //! - %Logical: `||`, `&&`, `!` + //! - Member access: `*` (dereference), `[]` (array subscript) + //! - Other: `()` (function call) + //! + //! More complex functionality like the ability to compose placeholders + //! into larger function objects inline are not supported. This is on + //! purpose; you should either use C++14 generic lambdas or a library + //! like [Boost.Phoenix][] if you need bigger guns. The goal here is + //! to save you a couple of characters in simple situations. + //! + //! ### Example + //! @include example/functional/placeholder.cpp + //! + //! [Boost.Phoenix]: http://www.boost.org/doc/libs/release/libs/phoenix/doc/html/index.html +#ifdef BOOST_HANA_DOXYGEN_INVOKED + constexpr unspecified _{}; +#else + namespace placeholder_detail { + template <typename I> + struct subscript { + I i; + + template <typename Xs, typename ...Z> + constexpr auto operator()(Xs&& xs, Z const& ...) const& + -> decltype(static_cast<Xs&&>(xs)[i]) + { return static_cast<Xs&&>(xs)[i]; } + + template <typename Xs, typename ...Z> + constexpr auto operator()(Xs&& xs, Z const& ...) & + -> decltype(static_cast<Xs&&>(xs)[i]) + { return static_cast<Xs&&>(xs)[i]; } + + template <typename Xs, typename ...Z> + constexpr auto operator()(Xs&& xs, Z const& ...) && + -> decltype(static_cast<Xs&&>(xs)[std::declval<I>()]) + { return static_cast<Xs&&>(xs)[std::move(i)]; } + }; + + template <typename F, typename Xs, std::size_t ...i> + constexpr decltype(auto) invoke_impl(F&& f, Xs&& xs, std::index_sequence<i...>) { + return static_cast<F&&>(f)(hana::get_impl<i>(static_cast<Xs&&>(xs).storage_)...); + } + + template <typename ...X> + struct invoke; + + struct placeholder { + struct secret { }; + + template <typename X> + constexpr decltype(auto) operator[](X&& x) const + { return detail::create<subscript>{}(static_cast<X&&>(x)); } + + template <typename ...X> + constexpr invoke<typename detail::decay<X>::type...> + operator()(X&& ...x) const { + return {secret{}, static_cast<X&&>(x)...}; + } + }; + + template <typename ...X> + struct invoke { + template <typename ...Y> + constexpr invoke(placeholder::secret, Y&& ...y) + : storage_{static_cast<Y&&>(y)...} + { } + + basic_tuple<X...> storage_; + + template <typename F, typename ...Z> + constexpr auto operator()(F&& f, Z const& ...) const& -> decltype( + static_cast<F&&>(f)(std::declval<X const&>()...) + ) { + return invoke_impl(static_cast<F&&>(f), *this, + std::make_index_sequence<sizeof...(X)>{}); + } + + template <typename F, typename ...Z> + constexpr auto operator()(F&& f, Z const& ...) & -> decltype( + static_cast<F&&>(f)(std::declval<X&>()...) + ) { + return invoke_impl(static_cast<F&&>(f), *this, + std::make_index_sequence<sizeof...(X)>{}); + } + + template <typename F, typename ...Z> + constexpr auto operator()(F&& f, Z const& ...) && -> decltype( + static_cast<F&&>(f)(std::declval<X&&>()...) + ) { + return invoke_impl(static_cast<F&&>(f), static_cast<invoke&&>(*this), + std::make_index_sequence<sizeof...(X)>{}); + } + }; + +#define BOOST_HANA_PLACEHOLDER_BINARY_OP(op, op_name) \ + template <typename X> \ + struct op_name ## _left { \ + X x; \ + \ + template <typename Y, typename ...Z> \ + constexpr auto operator()(Y&& y, Z const& ...) const& -> decltype( \ + std::declval<X const&>() op static_cast<Y&&>(y)) \ + { return x op static_cast<Y&&>(y); } \ + \ + template <typename Y, typename ...Z> \ + constexpr auto operator()(Y&& y, Z const& ...) & -> decltype( \ + std::declval<X&>() op static_cast<Y&&>(y)) \ + { return x op static_cast<Y&&>(y); } \ + \ + template <typename Y, typename ...Z> \ + constexpr auto operator()(Y&& y, Z const& ...) && -> decltype( \ + std::declval<X>() op static_cast<Y&&>(y)) \ + { return std::move(x) op static_cast<Y&&>(y); } \ + }; \ + \ + template <typename Y> \ + struct op_name ## _right { \ + Y y; \ + \ + template <typename X, typename ...Z> \ + constexpr auto operator()(X&& x, Z const& ...) const& -> decltype( \ + static_cast<X&&>(x) op std::declval<Y const&>()) \ + { return static_cast<X&&>(x) op y; } \ + \ + template <typename X, typename ...Z> \ + constexpr auto operator()(X&& x, Z const& ...) & -> decltype( \ + static_cast<X&&>(x) op std::declval<Y&>()) \ + { return static_cast<X&&>(x) op y; } \ + \ + template <typename X, typename ...Z> \ + constexpr auto operator()(X&& x, Z const& ...) && -> decltype( \ + static_cast<X&&>(x) op std::declval<Y>()) \ + { return static_cast<X&&>(x) op std::move(y); } \ + }; \ + \ + struct op_name { \ + template <typename X, typename Y, typename ...Z> \ + constexpr auto operator()(X&& x, Y&& y, Z const& ...) const -> decltype(\ + static_cast<X&&>(x) op static_cast<Y&&>(y)) \ + { return static_cast<X&&>(x) op static_cast<Y&&>(y); } \ + }; \ + \ + template <typename X> \ + constexpr decltype(auto) operator op (X&& x, placeholder) \ + { return detail::create<op_name ## _left>{}(static_cast<X&&>(x)); } \ + \ + template <typename Y> \ + constexpr decltype(auto) operator op (placeholder, Y&& y) \ + { return detail::create<op_name ## _right>{}(static_cast<Y&&>(y)); } \ + \ + inline constexpr decltype(auto) operator op (placeholder, placeholder) \ + { return op_name{}; } \ +/**/ + +#define BOOST_HANA_PLACEHOLDER_UNARY_OP(op, op_name) \ + struct op_name { \ + template <typename X, typename ...Z> \ + constexpr auto operator()(X&& x, Z const& ...) const \ + -> decltype(op static_cast<X&&>(x)) \ + { return op static_cast<X&&>(x); } \ + }; \ + \ + inline constexpr decltype(auto) operator op (placeholder) \ + { return op_name{}; } \ +/**/ + // Arithmetic + BOOST_HANA_PLACEHOLDER_UNARY_OP(+, unary_plus) + BOOST_HANA_PLACEHOLDER_UNARY_OP(-, unary_minus) + BOOST_HANA_PLACEHOLDER_BINARY_OP(+, plus) + BOOST_HANA_PLACEHOLDER_BINARY_OP(-, minus) + BOOST_HANA_PLACEHOLDER_BINARY_OP(*, times) + BOOST_HANA_PLACEHOLDER_BINARY_OP(/, divide) + BOOST_HANA_PLACEHOLDER_BINARY_OP(%, modulo) + + // Bitwise + BOOST_HANA_PLACEHOLDER_UNARY_OP(~, bitwise_not) + BOOST_HANA_PLACEHOLDER_BINARY_OP(&, bitwise_and) + BOOST_HANA_PLACEHOLDER_BINARY_OP(|, bitwise_or) + BOOST_HANA_PLACEHOLDER_BINARY_OP(^, bitwise_xor) + BOOST_HANA_PLACEHOLDER_BINARY_OP(<<, left_shift) + BOOST_HANA_PLACEHOLDER_BINARY_OP(>>, right_shift) + + // Comparison + BOOST_HANA_PLACEHOLDER_BINARY_OP(==, equal) + BOOST_HANA_PLACEHOLDER_BINARY_OP(!=, not_equal) + BOOST_HANA_PLACEHOLDER_BINARY_OP(<, less) + BOOST_HANA_PLACEHOLDER_BINARY_OP(<=, less_equal) + BOOST_HANA_PLACEHOLDER_BINARY_OP(>, greater) + BOOST_HANA_PLACEHOLDER_BINARY_OP(>=, greater_equal) + + // Logical + BOOST_HANA_PLACEHOLDER_BINARY_OP(||, logical_or) + BOOST_HANA_PLACEHOLDER_BINARY_OP(&&, logical_and) + BOOST_HANA_PLACEHOLDER_UNARY_OP(!, logical_not) + + // Member access (array subscript is a member function) + BOOST_HANA_PLACEHOLDER_UNARY_OP(*, dereference) + + // Other (function call is a member function) + +#undef BOOST_HANA_PREFIX_PLACEHOLDER_OP +#undef BOOST_HANA_BINARY_PLACEHOLDER_OP + } // end namespace placeholder_detail + + constexpr placeholder_detail::placeholder _{}; +#endif +BOOST_HANA_NAMESPACE_END + +#endif // !BOOST_HANA_FUNCTIONAL_PLACEHOLDER_HPP diff --git a/boost/hana/functional/reverse_partial.hpp b/boost/hana/functional/reverse_partial.hpp new file mode 100644 index 0000000000..9a2f63ff23 --- /dev/null +++ b/boost/hana/functional/reverse_partial.hpp @@ -0,0 +1,101 @@ +/*! +@file +Defines `boost::hana::reverse_partial`. + +@copyright Louis Dionne 2013-2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_HANA_FUNCTIONAL_REVERSE_PARTIAL_HPP +#define BOOST_HANA_FUNCTIONAL_REVERSE_PARTIAL_HPP + +#include <boost/hana/basic_tuple.hpp> +#include <boost/hana/config.hpp> +#include <boost/hana/detail/decay.hpp> + +#include <cstddef> +#include <utility> + + +BOOST_HANA_NAMESPACE_BEGIN + //! @ingroup group-functional + //! Partially apply a function to some arguments. + //! + //! Given a function `f` and some arguments, `reverse_partial` returns a + //! new function corresponding to `f` whose last arguments are partially + //! applied. Specifically, `reverse_partial(f, x...)` is a function such + //! that + //! @code + //! reverse_partial(f, x...)(y...) == f(y..., x...) + //! @endcode + //! + //! @note + //! The arity of `f` must match the total number of arguments passed to + //! it, i.e. `sizeof...(x) + sizeof...(y)`. + //! + //! + //! Example + //! ------- + //! @include example/functional/reverse_partial.cpp +#ifdef BOOST_HANA_DOXYGEN_INVOKED + constexpr auto reverse_partial = [](auto&& f, auto&& ...x) { + return [perfect-capture](auto&& ...y) -> decltype(auto) { + return forwarded(f)(forwarded(y)..., forwarded(x)...); + }; + }; +#else + template <typename Indices, typename F, typename ...X> + struct reverse_partial_t; + + struct make_reverse_partial_t { + struct secret { }; + template <typename F, typename ...X> + constexpr reverse_partial_t< + std::make_index_sequence<sizeof...(X)>, + typename detail::decay<F>::type, + typename detail::decay<X>::type... + > operator()(F&& f, X&& ...x) const { + return {secret{}, static_cast<F&&>(f), static_cast<X&&>(x)...}; + } + }; + + template <std::size_t ...n, typename F, typename ...X> + struct reverse_partial_t<std::index_sequence<n...>, F, X...> { + template <typename ...T> + constexpr reverse_partial_t(make_reverse_partial_t::secret, T&& ...t) + : storage_{static_cast<T&&>(t)...} + { } + + basic_tuple<F, X...> storage_; + + template <typename ...Y> + constexpr decltype(auto) operator()(Y&& ...y) const& { + return hana::get_impl<0>(storage_)( + static_cast<Y&&>(y)..., + hana::get_impl<n+1>(storage_)... + ); + } + + template <typename ...Y> + constexpr decltype(auto) operator()(Y&& ...y) & { + return hana::get_impl<0>(storage_)( + static_cast<Y&&>(y)..., + hana::get_impl<n+1>(storage_)... + ); + } + + template <typename ...Y> + constexpr decltype(auto) operator()(Y&& ...y) && { + return static_cast<F&&>(hana::get_impl<0>(storage_))( + static_cast<Y&&>(y)..., + static_cast<X&&>(hana::get_impl<n+1>(storage_))... + ); + } + }; + + constexpr make_reverse_partial_t reverse_partial{}; +#endif +BOOST_HANA_NAMESPACE_END + +#endif // !BOOST_HANA_FUNCTIONAL_REVERSE_PARTIAL_HPP |