123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- // 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);
- }
- #if BOOST_STL_INTERFACES_USE_CONCEPTS
- template<typename T>
- requires std::invocable<F &&, T>
- #else
- template<
- typename T,
- typename Enable = std::enable_if_t<detail::is_invocable_v<F &&, T>>>
- #endif
- constexpr decltype(auto) operator()(T && t) &&
- {
- return std::move(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
|