319 lines
10 KiB
C++
319 lines
10 KiB
C++
// Copyright (C) 2022 T. Zachary Laine
|
|
//
|
|
// Distributed under the Boost Software License, Version 1.0. (See
|
|
// accompanying file LICENSE_1_0.txt or copy at
|
|
// http://www.boost.org/LICENSE_1_0.txt)
|
|
#ifndef BOOST_STL_INTERFACES_VIEW_ADAPTOR_HPP
|
|
#define BOOST_STL_INTERFACES_VIEW_ADAPTOR_HPP
|
|
|
|
#include <boost/stl_interfaces/config.hpp>
|
|
#include <boost/stl_interfaces/detail/view_closure.hpp>
|
|
|
|
#include <boost/type_traits/is_detected.hpp>
|
|
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
|
|
|
|
#if !defined(BOOST_STL_INTERFACES_DOXYGEN)
|
|
|
|
#if defined(__cpp_lib_ranges) && 202202L <= __cpp_lib_ranges
|
|
#define BOOST_STL_INTERFACES_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE 1
|
|
#else
|
|
#define BOOST_STL_INTERFACES_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE 0
|
|
#endif
|
|
|
|
#if !BOOST_STL_INTERFACES_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE && \
|
|
BOOST_STL_INTERFACES_USE_CONCEPTS && defined(__GNUC__) && 12 <= __GNUC__
|
|
#define BOOST_STL_INTERFACES_USE_LIBSTDCPP_GCC12_RANGE_ADAPTOR_CLOSURE 1
|
|
#else
|
|
#define BOOST_STL_INTERFACES_USE_LIBSTDCPP_GCC12_RANGE_ADAPTOR_CLOSURE 0
|
|
#endif
|
|
|
|
#if !BOOST_STL_INTERFACES_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE && \
|
|
defined(_MSC_VER) && _MSC_VER <= 1929
|
|
#define BOOST_STL_INTERFACES_NEED_VS_COMPATIBLE_RANGE_ADAPTOR_CLOSURE 1
|
|
#else
|
|
#define BOOST_STL_INTERFACES_NEED_VS_COMPATIBLE_RANGE_ADAPTOR_CLOSURE 0
|
|
#endif
|
|
|
|
#if !BOOST_STL_INTERFACES_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE && \
|
|
!BOOST_STL_INTERFACES_USE_LIBSTDCPP_GCC12_RANGE_ADAPTOR_CLOSURE && \
|
|
!BOOST_STL_INTERFACES_NEED_VS_COMPATIBLE_RANGE_ADAPTOR_CLOSURE
|
|
#define BOOST_STL_INTERFACES_DEFINE_CUSTOM_RANGE_ADAPTOR_CLOSURE 1
|
|
#else
|
|
#define BOOST_STL_INTERFACES_DEFINE_CUSTOM_RANGE_ADAPTOR_CLOSURE 0
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
namespace boost { namespace stl_interfaces {
|
|
namespace detail {
|
|
template<typename F, typename... Args>
|
|
using invocable_expr =
|
|
decltype(std::declval<F>()(std::declval<Args>()...));
|
|
template<typename F, typename... Args>
|
|
constexpr bool is_invocable_v =
|
|
is_detected_v<invocable_expr, F, Args...>;
|
|
|
|
template<typename Func, typename... CapturedArgs>
|
|
struct bind_back_t
|
|
{
|
|
static_assert(std::is_move_constructible<Func>::value, "");
|
|
#if defined(__cpp_fold_expressions)
|
|
static_assert(
|
|
(std::is_move_constructible<CapturedArgs>::value && ...), "");
|
|
#endif
|
|
|
|
template<typename F, typename... Args>
|
|
explicit constexpr bind_back_t(int, F && f, Args &&... args) :
|
|
f_((F &&) f), bound_args_((Args &&) args...)
|
|
{
|
|
static_assert(sizeof...(Args) == sizeof...(CapturedArgs), "");
|
|
}
|
|
|
|
template<typename... Args>
|
|
constexpr decltype(auto) operator()(Args &&... args) &
|
|
{
|
|
return call_impl(*this, indices(), (Args &&) args...);
|
|
}
|
|
|
|
template<typename... Args>
|
|
constexpr decltype(auto) operator()(Args &&... args) const &
|
|
{
|
|
return call_impl(*this, indices(), (Args &&) args...);
|
|
}
|
|
|
|
template<typename... Args>
|
|
constexpr decltype(auto) operator()(Args &&... args) &&
|
|
{
|
|
return call_impl(
|
|
std::move(*this), indices(), (Args &&) args...);
|
|
}
|
|
|
|
template<typename... Args>
|
|
constexpr decltype(auto) operator()(Args &&... args) const &&
|
|
{
|
|
return call_impl(
|
|
std::move(*this), indices(), (Args &&) args...);
|
|
}
|
|
|
|
private:
|
|
using indices = std::index_sequence_for<CapturedArgs...>;
|
|
|
|
template<typename T, size_t... I, typename... Args>
|
|
static constexpr decltype(auto)
|
|
call_impl(T && this_, std::index_sequence<I...>, Args &&... args)
|
|
{
|
|
return ((T &&) this_)
|
|
.f_((Args &&) args...,
|
|
std::get<I>(((T &&) this_).bound_args_)...);
|
|
}
|
|
|
|
Func f_;
|
|
std::tuple<CapturedArgs...> bound_args_;
|
|
};
|
|
|
|
template<typename Func, typename... Args>
|
|
using bind_back_result =
|
|
bind_back_t<std::decay_t<Func>, std::decay_t<Args>...>;
|
|
}
|
|
|
|
/** An implementation of `std::bind_back()` from C++23. */
|
|
template<typename Func, typename... Args>
|
|
constexpr auto bind_back(Func && f, Args &&... args)
|
|
{
|
|
return detail::bind_back_result<Func, Args...>(
|
|
0, (Func &&) f, (Args &&) args...);
|
|
}
|
|
|
|
#if BOOST_STL_INTERFACES_DEFINE_CUSTOM_RANGE_ADAPTOR_CLOSURE || \
|
|
defined(BOOST_STL_INTERFACES_DOXYGEN)
|
|
|
|
/** A backwards-compatible implementation of C++23's
|
|
`std::ranges::range_adaptor_closure`. `range_adaptor_closure` may be
|
|
a struct template or may be an alias, as required to maintain
|
|
compatability with the standard library's view adaptors. */
|
|
#if BOOST_STL_INTERFACES_USE_CONCEPTS
|
|
template<typename D>
|
|
requires std::is_class_v<D> && std::same_as<D, std::remove_cv_t<D>>
|
|
#else
|
|
template<
|
|
typename D,
|
|
typename Enable = std::enable_if_t<
|
|
std::is_class<D>::value &&
|
|
std::is_same<D, std::remove_cv_t<D>>::value>>
|
|
#endif
|
|
struct range_adaptor_closure;
|
|
|
|
namespace detail {
|
|
#if BOOST_STL_INTERFACES_USE_CONCEPTS
|
|
template<typename T>
|
|
concept range_adaptor_closure_ = std::derived_from<
|
|
std::remove_cvref_t<T>,
|
|
range_adaptor_closure<std::remove_cvref_t<T>>>;
|
|
#else
|
|
template<typename T>
|
|
using range_adaptor_closure_tag_expr = typename range_adaptor_closure<
|
|
T>::inheritance_tag_with_an_unlikely_name_;
|
|
template<typename T>
|
|
constexpr bool range_adaptor_closure_ =
|
|
is_detected_v<range_adaptor_closure_tag_expr, remove_cvref_t<T>>;
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
|
|
#if BOOST_STL_INTERFACES_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE
|
|
|
|
template<typename D>
|
|
using range_adaptor_closure = std::ranges::range_adaptor_closure<D>;
|
|
|
|
#elif BOOST_STL_INTERFACES_USE_LIBSTDCPP_GCC12_RANGE_ADAPTOR_CLOSURE
|
|
|
|
template<typename D>
|
|
using range_adaptor_closure = std::views::__adaptor::_RangeAdaptorClosure;
|
|
|
|
#elif BOOST_STL_INTERFACES_NEED_VS_COMPATIBLE_RANGE_ADAPTOR_CLOSURE
|
|
|
|
template<typename D>
|
|
using range_adaptor_closure = detail::pipeable<D>;
|
|
|
|
#else
|
|
|
|
#if BOOST_STL_INTERFACES_USE_CONCEPTS
|
|
template<typename D>
|
|
requires std::is_class_v<D> && std::same_as<D, std::remove_cv_t<D>>
|
|
#else
|
|
template<typename D, typename>
|
|
#endif
|
|
struct range_adaptor_closure
|
|
{
|
|
#if BOOST_STL_INTERFACES_USE_CONCEPTS
|
|
template<typename T>
|
|
requires std::invocable<D, T>
|
|
#else
|
|
template<
|
|
typename T,
|
|
typename Enable = std::enable_if_t<detail::is_invocable_v<D, T>>>
|
|
#endif
|
|
[[nodiscard]] friend constexpr decltype(auto) operator|(T && t, D && d)
|
|
{
|
|
return std::move(d)((T &&) t);
|
|
}
|
|
|
|
#if BOOST_STL_INTERFACES_USE_CONCEPTS
|
|
template<typename T>
|
|
requires std::invocable<D const &, T>
|
|
#else
|
|
template<
|
|
typename T,
|
|
typename Enable =
|
|
std::enable_if_t<detail::is_invocable_v<D const &, T>>>
|
|
#endif
|
|
[[nodiscard]] friend constexpr decltype(auto)
|
|
operator|(T && t, D const & d)
|
|
{
|
|
return d((T &&) t);
|
|
}
|
|
|
|
using inheritance_tag_with_an_unlikely_name_ = int;
|
|
};
|
|
|
|
#endif
|
|
|
|
//[closure_defn
|
|
/** An invocable consisting of a contained invocable `f`. Calling
|
|
`operator()` with some argument `t` calls `f(t)` and returns the
|
|
result. This type is typically used to capture a the result of a call
|
|
to `bind_back()`. */
|
|
template<typename F>
|
|
struct closure : range_adaptor_closure<closure<F>>
|
|
{
|
|
constexpr closure(F f) : f_(f) {}
|
|
|
|
#if BOOST_STL_INTERFACES_USE_CONCEPTS
|
|
template<typename T>
|
|
requires std::invocable<F const &, T>
|
|
#else
|
|
template<
|
|
typename T,
|
|
typename Enable =
|
|
std::enable_if_t<detail::is_invocable_v<F const &, T>>>
|
|
#endif
|
|
constexpr decltype(auto) operator()(T && t) const
|
|
{
|
|
return f_((T &&) t);
|
|
}
|
|
|
|
private:
|
|
F f_;
|
|
};
|
|
//]
|
|
|
|
namespace detail {
|
|
#if !BOOST_STL_INTERFACES_USE_CONCEPTS
|
|
template<typename F, bool Invocable, typename... Args>
|
|
struct adaptor_impl
|
|
{
|
|
static constexpr decltype(auto) call(F const & f, Args &&... args)
|
|
{
|
|
return f((Args &&) args...);
|
|
}
|
|
};
|
|
|
|
template<typename F, typename... Args>
|
|
struct adaptor_impl<F, false, Args...>
|
|
{
|
|
static constexpr auto call(F const & f, Args &&... args)
|
|
{
|
|
using closure_func =
|
|
std::decay_t<decltype(stl_interfaces::bind_back(
|
|
f, (Args &&) args...))>;
|
|
return closure<closure_func>(
|
|
stl_interfaces::bind_back(f, (Args &&) args...));
|
|
}
|
|
};
|
|
#endif
|
|
}
|
|
|
|
//[adaptor_defn
|
|
/** Adapts an invocable `f` as a view adaptor. Calling
|
|
`operator(args...)` will either: call `f(args...)` and return the
|
|
result, if `f(args...)` is well-formed; or return
|
|
`closure(stl_interfaces::bind_back(f, args...))` otherwise. */
|
|
template<typename F>
|
|
struct adaptor
|
|
{
|
|
constexpr adaptor(F f) : f_(f) {}
|
|
|
|
// clang-format off
|
|
template<typename... Args>
|
|
constexpr auto operator()(Args &&... args) const
|
|
// clang-format on
|
|
{
|
|
#if BOOST_STL_INTERFACES_USE_CONCEPTS
|
|
if constexpr (std::is_invocable_v<F const &, Args...>) {
|
|
return f((Args &&) args...);
|
|
} else {
|
|
return closure(
|
|
stl_interfaces::bind_back(f_, (Args &&) args...));
|
|
}
|
|
#else
|
|
return detail::adaptor_impl<
|
|
F const &,
|
|
detail::is_invocable_v<F const &, Args...>,
|
|
Args...>::call(f_, (Args &&) args...);
|
|
#endif
|
|
}
|
|
|
|
private:
|
|
F f_;
|
|
};
|
|
//]
|
|
|
|
}}
|
|
|
|
#endif
|