1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
|
// Copyright 2022 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// -----------------------------------------------------------------------------
// File: any_invocable.h
// -----------------------------------------------------------------------------
//
// This header file defines an `y_absl::AnyInvocable` type that assumes ownership
// and wraps an object of an invocable type. (Invocable types adhere to the
// concept specified in https://en.cppreference.com/w/cpp/concepts/invocable.)
//
// In general, prefer `y_absl::AnyInvocable` when you need a type-erased
// function parameter that needs to take ownership of the type.
//
// NOTE: `y_absl::AnyInvocable` is similar to the C++23 `std::move_only_function`
// abstraction, but has a slightly different API and is not designed to be a
// drop-in replacement or C++11-compatible backfill of that type.
//
// Credits to Matt Calabrese (https://github.com/mattcalabrese) for the original
// implementation.
#ifndef Y_ABSL_FUNCTIONAL_ANY_INVOCABLE_H_
#define Y_ABSL_FUNCTIONAL_ANY_INVOCABLE_H_
#include <cstddef>
#include <initializer_list>
#include <type_traits>
#include <utility>
#include "y_absl/base/config.h"
#include "y_absl/functional/internal/any_invocable.h"
#include "y_absl/meta/type_traits.h"
#include "y_absl/utility/utility.h"
namespace y_absl {
Y_ABSL_NAMESPACE_BEGIN
// y_absl::AnyInvocable
//
// `y_absl::AnyInvocable` is a functional wrapper type, like `std::function`, that
// assumes ownership of an invocable object. Unlike `std::function`, an
// `y_absl::AnyInvocable` is more type-safe and provides the following additional
// benefits:
//
// * Properly adheres to const correctness of the underlying type
// * Is move-only so avoids concurrency problems with copied invocables and
// unnecessary copies in general.
// * Supports reference qualifiers allowing it to perform unique actions (noted
// below).
//
// `y_absl::AnyInvocable` is a template, and an `y_absl::AnyInvocable` instantiation
// may wrap any invocable object with a compatible function signature, e.g.
// having arguments and return types convertible to types matching the
// `y_absl::AnyInvocable` signature, and also matching any stated reference
// qualifiers, as long as that type is moveable. It therefore provides broad
// type erasure for functional objects.
//
// An `y_absl::AnyInvocable` is typically used as a type-erased function parameter
// for accepting various functional objects:
//
// // Define a function taking an AnyInvocable parameter.
// void my_func(y_absl::AnyInvocable<int()> f) {
// ...
// };
//
// // That function can accept any invocable type:
//
// // Accept a function reference. We don't need to move a reference.
// int func1() { return 0; };
// my_func(func1);
//
// // Accept a lambda. We use std::move here because otherwise my_func would
// // copy the lambda.
// auto lambda = []() { return 0; };
// my_func(std::move(lambda));
//
// // Accept a function pointer. We don't need to move a function pointer.
// func2 = &func1;
// my_func(func2);
//
// // Accept an std::function by moving it. Note that the lambda is copyable
// // (satisfying std::function requirements) and moveable (satisfying
// // y_absl::AnyInvocable requirements).
// std::function<int()> func6 = []() { return 0; };
// my_func(std::move(func6));
//
// `AnyInvocable` also properly respects `const` qualifiers, reference
// qualifiers, and the `noexcept` specification (only in C++ 17 and beyond) as
// part of the user-specified function type (e.g.
// `AnyInvocable<void()&& const noexcept>`). These qualifiers will be applied to
// the `AnyInvocable` object's `operator()`, and the underlying invocable must
// be compatible with those qualifiers.
//
// Comparison of const and non-const function types:
//
// // Store a closure inside of `func` with the function type `int()`.
// // Note that we have made `func` itself `const`.
// const AnyInvocable<int()> func = [](){ return 0; };
//
// func(); // Compile-error: the passed type `int()` isn't `const`.
//
// // Store a closure inside of `const_func` with the function type
// // `int() const`.
// // Note that we have also made `const_func` itself `const`.
// const AnyInvocable<int() const> const_func = [](){ return 0; };
//
// const_func(); // Fine: `int() const` is `const`.
//
// In the above example, the call `func()` would have compiled if
// `std::function` were used even though the types are not const compatible.
// This is a bug, and using `y_absl::AnyInvocable` properly detects that bug.
//
// In addition to affecting the signature of `operator()`, the `const` and
// reference qualifiers of the function type also appropriately constrain which
// kinds of invocable objects you are allowed to place into the `AnyInvocable`
// instance. If you specify a function type that is const-qualified, then
// anything that you attempt to put into the `AnyInvocable` must be callable on
// a `const` instance of that type.
//
// Constraint example:
//
// // Fine because the lambda is callable when `const`.
// AnyInvocable<int() const> func = [=](){ return 0; };
//
// // This is a compile-error because the lambda isn't callable when `const`.
// AnyInvocable<int() const> error = [=]() mutable { return 0; };
//
// An `&&` qualifier can be used to express that an `y_absl::AnyInvocable`
// instance should be invoked at most once:
//
// // Invokes `continuation` with the logical result of an operation when
// // that operation completes (common in asynchronous code).
// void CallOnCompletion(AnyInvocable<void(int)&&> continuation) {
// int result_of_foo = foo();
//
// // `std::move` is required because the `operator()` of `continuation` is
// // rvalue-reference qualified.
// std::move(continuation)(result_of_foo);
// }
//
// Attempting to call `y_absl::AnyInvocable` multiple times in such a case
// results in undefined behavior.
template <class Sig>
class AnyInvocable : private internal_any_invocable::Impl<Sig> {
private:
static_assert(
std::is_function<Sig>::value,
"The template argument of AnyInvocable must be a function type.");
using Impl = internal_any_invocable::Impl<Sig>;
public:
// The return type of Sig
using result_type = typename Impl::result_type;
// Constructors
// Constructs the `AnyInvocable` in an empty state.
AnyInvocable() noexcept = default;
AnyInvocable(std::nullptr_t) noexcept {} // NOLINT
// Constructs the `AnyInvocable` from an existing `AnyInvocable` by a move.
// Note that `f` is not guaranteed to be empty after move-construction,
// although it may be.
AnyInvocable(AnyInvocable&& /*f*/) noexcept = default;
// Constructs an `AnyInvocable` from an invocable object.
//
// Upon construction, `*this` is only empty if `f` is a function pointer or
// member pointer type and is null, or if `f` is an `AnyInvocable` that is
// empty.
template <class F, typename = y_absl::enable_if_t<
internal_any_invocable::CanConvert<Sig, F>::value>>
AnyInvocable(F&& f) // NOLINT
: Impl(internal_any_invocable::ConversionConstruct(),
std::forward<F>(f)) {}
// Constructs an `AnyInvocable` that holds an invocable object of type `T`,
// which is constructed in-place from the given arguments.
//
// Example:
//
// AnyInvocable<int(int)> func(
// y_absl::in_place_type<PossiblyImmovableType>, arg1, arg2);
//
template <class T, class... Args,
typename = y_absl::enable_if_t<
internal_any_invocable::CanEmplace<Sig, T, Args...>::value>>
explicit AnyInvocable(y_absl::in_place_type_t<T>, Args&&... args)
: Impl(y_absl::in_place_type<y_absl::decay_t<T>>,
std::forward<Args>(args)...) {
static_assert(std::is_same<T, y_absl::decay_t<T>>::value,
"The explicit template argument of in_place_type is required "
"to be an unqualified object type.");
}
// Overload of the above constructor to support list-initialization.
template <class T, class U, class... Args,
typename = y_absl::enable_if_t<internal_any_invocable::CanEmplace<
Sig, T, std::initializer_list<U>&, Args...>::value>>
explicit AnyInvocable(y_absl::in_place_type_t<T>,
std::initializer_list<U> ilist, Args&&... args)
: Impl(y_absl::in_place_type<y_absl::decay_t<T>>, ilist,
std::forward<Args>(args)...) {
static_assert(std::is_same<T, y_absl::decay_t<T>>::value,
"The explicit template argument of in_place_type is required "
"to be an unqualified object type.");
}
// Assignment Operators
// Assigns an `AnyInvocable` through move-assignment.
// Note that `f` is not guaranteed to be empty after move-assignment
// although it may be.
AnyInvocable& operator=(AnyInvocable&& /*f*/) noexcept = default;
// Assigns an `AnyInvocable` from a nullptr, clearing the `AnyInvocable`. If
// not empty, destroys the target, putting `*this` into an empty state.
AnyInvocable& operator=(std::nullptr_t) noexcept {
this->Clear();
return *this;
}
// Assigns an `AnyInvocable` from an existing `AnyInvocable` instance.
//
// Upon assignment, `*this` is only empty if `f` is a function pointer or
// member pointer type and is null, or if `f` is an `AnyInvocable` that is
// empty.
template <class F, typename = y_absl::enable_if_t<
internal_any_invocable::CanAssign<Sig, F>::value>>
AnyInvocable& operator=(F&& f) {
*this = AnyInvocable(std::forward<F>(f));
return *this;
}
// Assigns an `AnyInvocable` from a reference to an invocable object.
// Upon assignment, stores a reference to the invocable object in the
// `AnyInvocable` instance.
template <
class F,
typename = y_absl::enable_if_t<
internal_any_invocable::CanAssignReferenceWrapper<Sig, F>::value>>
AnyInvocable& operator=(std::reference_wrapper<F> f) noexcept {
*this = AnyInvocable(f);
return *this;
}
// Destructor
// If not empty, destroys the target.
~AnyInvocable() = default;
// y_absl::AnyInvocable::swap()
//
// Exchanges the targets of `*this` and `other`.
void swap(AnyInvocable& other) noexcept { std::swap(*this, other); }
// y_absl::AnyInvocable::operator bool()
//
// Returns `true` if `*this` is not empty.
//
// WARNING: An `AnyInvocable` that wraps an empty `std::function` is not
// itself empty. This behavior is consistent with the standard equivalent
// `std::move_only_function`.
//
// In other words:
// std::function<void()> f; // empty
// y_absl::AnyInvocable<void()> a = std::move(f); // not empty
explicit operator bool() const noexcept { return this->HasValue(); }
// Invokes the target object of `*this`. `*this` must not be empty.
//
// Note: The signature of this function call operator is the same as the
// template parameter `Sig`.
using Impl::operator();
// Equality operators
// Returns `true` if `*this` is empty.
friend bool operator==(const AnyInvocable& f, std::nullptr_t) noexcept {
return !f.HasValue();
}
// Returns `true` if `*this` is empty.
friend bool operator==(std::nullptr_t, const AnyInvocable& f) noexcept {
return !f.HasValue();
}
// Returns `false` if `*this` is empty.
friend bool operator!=(const AnyInvocable& f, std::nullptr_t) noexcept {
return f.HasValue();
}
// Returns `false` if `*this` is empty.
friend bool operator!=(std::nullptr_t, const AnyInvocable& f) noexcept {
return f.HasValue();
}
// swap()
//
// Exchanges the targets of `f1` and `f2`.
friend void swap(AnyInvocable& f1, AnyInvocable& f2) noexcept { f1.swap(f2); }
private:
// Friending other instantiations is necessary for conversions.
template <bool /*SigIsNoexcept*/, class /*ReturnType*/, class... /*P*/>
friend class internal_any_invocable::CoreImpl;
};
Y_ABSL_NAMESPACE_END
} // namespace y_absl
#endif // Y_ABSL_FUNCTIONAL_ANY_INVOCABLE_H_
|