186 lines
6.5 KiB
C++
186 lines
6.5 KiB
C++
/*!
|
|
@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/detail/decay.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 detail::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
|