1// Copyright (C) 2022 T. Zachary Laine
2//
3// Distributed under the Boost Software License, Version 1.0. (See
4// accompanying file LICENSE_1_0.txt or copy at
5// http://www.boost.org/LICENSE_1_0.txt)
6#ifndef BOOST_STL_INTERFACES_VIEW_ADAPTOR_HPP
7#define BOOST_STL_INTERFACES_VIEW_ADAPTOR_HPP
8
9#include <boost/stl_interfaces/config.hpp>
10#include <boost/stl_interfaces/detail/view_closure.hpp>
11
12#include <boost/type_traits/is_detected.hpp>
13
14#include <tuple>
15#include <type_traits>
16
17
18#if !defined(BOOST_STL_INTERFACES_DOXYGEN)
19
20#if defined(__cpp_lib_ranges) && 202202L <= __cpp_lib_ranges
21#define BOOST_STL_INTERFACES_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE 1
22#else
23#define BOOST_STL_INTERFACES_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE 0
24#endif
25
26#if !BOOST_STL_INTERFACES_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE && \
27 BOOST_STL_INTERFACES_USE_CONCEPTS && defined(__GNUC__) && 12 <= __GNUC__
28#define BOOST_STL_INTERFACES_USE_LIBSTDCPP_GCC12_RANGE_ADAPTOR_CLOSURE 1
29#else
30#define BOOST_STL_INTERFACES_USE_LIBSTDCPP_GCC12_RANGE_ADAPTOR_CLOSURE 0
31#endif
32
33#if !BOOST_STL_INTERFACES_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE && \
34 defined(_MSC_VER) && _MSC_VER <= 1929
35#define BOOST_STL_INTERFACES_NEED_VS_COMPATIBLE_RANGE_ADAPTOR_CLOSURE 1
36#else
37#define BOOST_STL_INTERFACES_NEED_VS_COMPATIBLE_RANGE_ADAPTOR_CLOSURE 0
38#endif
39
40#if !BOOST_STL_INTERFACES_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE && \
41 !BOOST_STL_INTERFACES_USE_LIBSTDCPP_GCC12_RANGE_ADAPTOR_CLOSURE && \
42 !BOOST_STL_INTERFACES_NEED_VS_COMPATIBLE_RANGE_ADAPTOR_CLOSURE
43#define BOOST_STL_INTERFACES_DEFINE_CUSTOM_RANGE_ADAPTOR_CLOSURE 1
44#else
45#define BOOST_STL_INTERFACES_DEFINE_CUSTOM_RANGE_ADAPTOR_CLOSURE 0
46#endif
47
48#endif
49
50
51namespace boost { namespace stl_interfaces {
52 namespace detail {
53 template<typename F, typename... Args>
54 using invocable_expr =
55 decltype(std::declval<F>()(std::declval<Args>()...));
56 template<typename F, typename... Args>
57 constexpr bool is_invocable_v =
58 is_detected_v<invocable_expr, F, Args...>;
59
60 template<typename Func, typename... CapturedArgs>
61 struct bind_back_t
62 {
63 static_assert(std::is_move_constructible<Func>::value, "");
64#if defined(__cpp_fold_expressions)
65 static_assert(
66 (std::is_move_constructible<CapturedArgs>::value && ...), "");
67#endif
68
69 template<typename F, typename... Args>
70 explicit constexpr bind_back_t(int, F && f, Args &&... args) :
71 f_((F &&) f), bound_args_((Args &&) args...)
72 {
73 static_assert(sizeof...(Args) == sizeof...(CapturedArgs), "");
74 }
75
76 template<typename... Args>
77 constexpr decltype(auto) operator()(Args &&... args) &
78 {
79 return call_impl(*this, indices(), (Args &&) args...);
80 }
81
82 template<typename... Args>
83 constexpr decltype(auto) operator()(Args &&... args) const &
84 {
85 return call_impl(*this, indices(), (Args &&) args...);
86 }
87
88 template<typename... Args>
89 constexpr decltype(auto) operator()(Args &&... args) &&
90 {
91 return call_impl(
92 std::move(*this), indices(), (Args &&) args...);
93 }
94
95 template<typename... Args>
96 constexpr decltype(auto) operator()(Args &&... args) const &&
97 {
98 return call_impl(
99 std::move(*this), indices(), (Args &&) args...);
100 }
101
102 private:
103 using indices = std::index_sequence_for<CapturedArgs...>;
104
105 template<typename T, size_t... I, typename... Args>
106 static constexpr decltype(auto)
107 call_impl(T && this_, std::index_sequence<I...>, Args &&... args)
108 {
109 return ((T &&) this_)
110 .f_((Args &&) args...,
111 std::get<I>(((T &&) this_).bound_args_)...);
112 }
113
114 Func f_;
115 std::tuple<CapturedArgs...> bound_args_;
116 };
117
118 template<typename Func, typename... Args>
119 using bind_back_result =
120 bind_back_t<std::decay_t<Func>, std::decay_t<Args>...>;
121 }
122
123 /** An implementation of `std::bind_back()` from C++23. */
124 template<typename Func, typename... Args>
125 constexpr auto bind_back(Func && f, Args &&... args)
126 {
127 return detail::bind_back_result<Func, Args...>(
128 0, (Func &&) f, (Args &&) args...);
129 }
130
131#if BOOST_STL_INTERFACES_DEFINE_CUSTOM_RANGE_ADAPTOR_CLOSURE || \
132 defined(BOOST_STL_INTERFACES_DOXYGEN)
133
134 /** A backwards-compatible implementation of C++23's
135 `std::ranges::range_adaptor_closure`. `range_adaptor_closure` may be
136 a struct template or may be an alias, as required to maintain
137 compatability with the standard library's view adaptors. */
138#if BOOST_STL_INTERFACES_USE_CONCEPTS
139 template<typename D>
140 requires std::is_class_v<D> && std::same_as<D, std::remove_cv_t<D>>
141#else
142 template<
143 typename D,
144 typename Enable = std::enable_if_t<
145 std::is_class<D>::value &&
146 std::is_same<D, std::remove_cv_t<D>>::value>>
147#endif
148 struct range_adaptor_closure;
149
150 namespace detail {
151#if BOOST_STL_INTERFACES_USE_CONCEPTS
152 template<typename T>
153 concept range_adaptor_closure_ = std::derived_from<
154 std::remove_cvref_t<T>,
155 range_adaptor_closure<std::remove_cvref_t<T>>>;
156#else
157 template<typename T>
158 using range_adaptor_closure_tag_expr = typename range_adaptor_closure<
159 T>::inheritance_tag_with_an_unlikely_name_;
160 template<typename T>
161 constexpr bool range_adaptor_closure_ =
162 is_detected_v<range_adaptor_closure_tag_expr, remove_cvref_t<T>>;
163#endif
164 }
165
166#endif
167
168#if BOOST_STL_INTERFACES_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE
169
170 template<typename D>
171 using range_adaptor_closure = std::ranges::range_adaptor_closure<D>;
172
173#elif BOOST_STL_INTERFACES_USE_LIBSTDCPP_GCC12_RANGE_ADAPTOR_CLOSURE
174
175 template<typename D>
176 using range_adaptor_closure = std::views::__adaptor::_RangeAdaptorClosure;
177
178#elif BOOST_STL_INTERFACES_NEED_VS_COMPATIBLE_RANGE_ADAPTOR_CLOSURE
179
180 template<typename D>
181 using range_adaptor_closure = detail::pipeable<D>;
182
183#else
184
185#if BOOST_STL_INTERFACES_USE_CONCEPTS
186 template<typename D>
187 requires std::is_class_v<D> && std::same_as<D, std::remove_cv_t<D>>
188#else
189 template<typename D, typename>
190#endif
191 struct range_adaptor_closure
192 {
193#if BOOST_STL_INTERFACES_USE_CONCEPTS
194 template<typename T>
195 requires std::invocable<D, T>
196#else
197 template<
198 typename T,
199 typename Enable = std::enable_if_t<detail::is_invocable_v<D, T>>>
200#endif
201 [[nodiscard]] friend constexpr decltype(auto) operator|(T && t, D && d)
202 {
203 return std::move(d)((T &&) t);
204 }
205
206#if BOOST_STL_INTERFACES_USE_CONCEPTS
207 template<typename T>
208 requires std::invocable<D const &, T>
209#else
210 template<
211 typename T,
212 typename Enable =
213 std::enable_if_t<detail::is_invocable_v<D const &, T>>>
214#endif
215 [[nodiscard]] friend constexpr decltype(auto)
216 operator|(T && t, D const & d)
217 {
218 return d((T &&) t);
219 }
220
221 using inheritance_tag_with_an_unlikely_name_ = int;
222 };
223
224#endif
225
226 //[closure_defn
227 /** An invocable consisting of a contained invocable `f`. Calling
228 `operator()` with some argument `t` calls `f(t)` and returns the
229 result. This type is typically used to capture a the result of a call
230 to `bind_back()`. */
231 template<typename F>
232 struct closure : range_adaptor_closure<closure<F>>
233 {
234 constexpr closure(F f) : f_(f) {}
235
236#if BOOST_STL_INTERFACES_USE_CONCEPTS
237 template<typename T>
238 requires std::invocable<F const &, T>
239#else
240 template<
241 typename T,
242 typename Enable =
243 std::enable_if_t<detail::is_invocable_v<F const &, T>>>
244#endif
245 constexpr decltype(auto) operator()(T && t) const &
246 {
247 return f_((T &&) t);
248 }
249
250#if BOOST_STL_INTERFACES_USE_CONCEPTS
251 template<typename T>
252 requires std::invocable<F &&, T>
253#else
254 template<
255 typename T,
256 typename Enable = std::enable_if_t<detail::is_invocable_v<F &&, T>>>
257#endif
258 constexpr decltype(auto) operator()(T && t) &&
259 {
260 return std::move(f_)((T &&) t);
261 }
262
263 private:
264 F f_;
265 };
266 //]
267
268 namespace detail {
269#if !BOOST_STL_INTERFACES_USE_CONCEPTS
270 template<typename F, bool Invocable, typename... Args>
271 struct adaptor_impl
272 {
273 static constexpr decltype(auto) call(F const & f, Args &&... args)
274 {
275 return f((Args &&) args...);
276 }
277 };
278
279 template<typename F, typename... Args>
280 struct adaptor_impl<F, false, Args...>
281 {
282 static constexpr auto call(F const & f, Args &&... args)
283 {
284 using closure_func =
285 std::decay_t<decltype(stl_interfaces::bind_back(
286 f, (Args &&) args...))>;
287 return closure<closure_func>(
288 stl_interfaces::bind_back(f, (Args &&) args...));
289 }
290 };
291#endif
292 }
293
294 //[adaptor_defn
295 /** Adapts an invocable `f` as a view adaptor. Calling
296 `operator(args...)` will either: call `f(args...)` and return the
297 result, if `f(args...)` is well-formed; or return
298 `closure(stl_interfaces::bind_back(f, args...))` otherwise. */
299 template<typename F>
300 struct adaptor
301 {
302 constexpr adaptor(F f) : f_(f) {}
303
304 // clang-format off
305 template<typename... Args>
306 constexpr auto operator()(Args &&... args) const
307 // clang-format on
308 {
309#if BOOST_STL_INTERFACES_USE_CONCEPTS
310 if constexpr (std::is_invocable_v<F const &, Args...>) {
311 return f_((Args &&) args...);
312 } else {
313 return closure(
314 stl_interfaces::bind_back(f_, (Args &&) args...));
315 }
316#else
317 return detail::adaptor_impl<
318 F const &,
319 detail::is_invocable_v<F const &, Args...>,
320 Args...>::call(f_, (Args &&) args...);
321#endif
322 }
323
324 private:
325 F f_;
326 };
327 //]
328
329}}
330
331#endif
332

source code of boost/libs/stl_interfaces/include/boost/stl_interfaces/view_adaptor.hpp