Skip to content

Commit 7b70cdc

Browse files
committed
Move FnRef details into a __private header
1 parent a057384 commit 7b70cdc

File tree

5 files changed

+193
-121
lines changed

5 files changed

+193
-121
lines changed

subspace/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ target_sources(subspace PUBLIC
4545
"containers/array.h"
4646
"containers/slice.h"
4747
"containers/vec.h"
48+
"fn/__private/callable_types.h"
4849
"fn/__private/fn_box_storage.h"
50+
"fn/__private/fn_ref_invoker.h"
4951
"fn/__private/signature.h"
5052
"fn/callable.h"
5153
"fn/fn.h"
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include <concepts>
18+
#include <type_traits>
19+
20+
namespace sus::fn::__private {
21+
22+
/// Whether a functor `F` is a function pointer that is callable with `Args...`
23+
/// and will return a value that can be stored as `R`.
24+
template <class F, class R, class... Args>
25+
concept FunctionPointer =
26+
std::is_pointer_v<std::decay_t<F>> && requires(F f, Args... args) {
27+
{ (*f)(args...) } -> std::convertible_to<R>;
28+
};
29+
30+
/// Whether a functor `T` is a function pointer.
31+
template <class T>
32+
concept IsFunctionPointer =
33+
std::is_pointer_v<T> && std::is_function_v<std::remove_pointer_t<T>>;
34+
35+
/// Whether a functor `T` can convert to a function pointer, typically this
36+
/// means it's a captureless lambda.
37+
template <class F>
38+
concept ConvertsToFunctionPointer = requires(F f) {
39+
{ +f } -> IsFunctionPointer;
40+
};
41+
42+
/// Whether a functor `F` is a callable object (with an `operator()`) that is
43+
/// once-callable as an rvalue with `Args...` and will return a value that can
44+
/// be stored as `R`.
45+
template <class F, class R, class... Args>
46+
concept CallableOnceMut =
47+
!FunctionPointer<F, R, Args...> && requires(F && f, Args... args) {
48+
{ ::sus::move(f)(args...) } -> std::convertible_to<R>;
49+
};
50+
51+
/// Whether a functor `F` is a callable object (with an `operator()`) that is
52+
/// mutable-callable as an lvalue with `Args...` and will return a value that
53+
/// can be stored as `R`.
54+
template <class F, class R, class... Args>
55+
concept CallableMut =
56+
!FunctionPointer<F, R, Args...> && requires(F & f, Args... args) {
57+
{ f(args...) } -> std::convertible_to<R>;
58+
};
59+
60+
/// Whether a functor `F` is a callable object (with an `operator()`) that is
61+
/// const-callable with `Args...` and will return a value that can be stored as
62+
/// `R`.
63+
template <class F, class R, class... Args>
64+
concept CallableConst =
65+
!FunctionPointer<F, R, Args...> && requires(const F& f, Args... args) {
66+
{ f(args...) } -> std::convertible_to<R>;
67+
};
68+
69+
} // namespace sus::fn::__private
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include "subspace/mem/forward.h"
18+
19+
namespace sus::fn::__private {
20+
21+
union Storage {
22+
void (*fnptr)();
23+
void* object;
24+
};
25+
26+
/// Functions to call a functor `F` that is stored in `Storage`. The choice of
27+
/// function encodes which member of `Storage` holds the functor.
28+
template <class F>
29+
struct Invoker {
30+
/// Calls the `F` in `Storage`, allowing mutable overlaods, when it is a
31+
/// function pointer.
32+
template <class R, class... Args>
33+
static R fnptr_call_mut(const union Storage& s, Args... args) {
34+
F f = reinterpret_cast<F>(s.fnptr);
35+
return (*f)(::sus::forward<Args>(args)...);
36+
}
37+
38+
/// Calls the `F` in `Storage`, as an lvalue, when it is a callable object.
39+
template <class R, class... Args>
40+
static R object_call_mut(const union Storage& s, Args... args) {
41+
F& f = *static_cast<F*>(s.object);
42+
return f(::sus::forward<Args>(args)...);
43+
}
44+
45+
/// Calls the `F` in `Storage`, as an rvalue, when it is a callable object.
46+
template <class R, class... Args>
47+
static R object_call_once(const union Storage& s, Args... args) {
48+
F& f = *static_cast<F*>(s.object);
49+
return ::sus::move(f)(::sus::forward<Args>(args)...);
50+
}
51+
52+
/// Calls the `F` in `Storage`, allowing only const overloads, when it is a
53+
/// function pointer.
54+
template <class R, class... Args>
55+
static R fnptr_call_const(const union Storage& s, Args... args) {
56+
const F f = reinterpret_cast<const F>(s.fnptr);
57+
return (*f)(::sus::forward<Args>(args)...);
58+
}
59+
60+
/// Calls the `F` in `Storage`, as a const object, when it is a callable
61+
/// object.
62+
template <class R, class... Args>
63+
static R object_call_const(const union Storage& s, Args... args) {
64+
const F& f = *static_cast<const F*>(s.object);
65+
return f(::sus::forward<Args>(args)...);
66+
}
67+
};
68+
69+
/// A function pointer type that matches all the invoke functions the `Invoker`.
70+
template <class R, class... CallArgs>
71+
using InvokeFnPtr = R (*)(const union Storage& s, CallArgs... args);
72+
73+
} // namespace sus::fn::__private

subspace/fn/fn_ref.h

Lines changed: 40 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414

1515
#pragma once
1616

17-
#include "subspace/fn/callable.h"
17+
#include "subspace/fn/__private/callable_types.h"
18+
#include "subspace/fn/__private/fn_ref_invoker.h"
1819
#include "subspace/macros/lifetimebound.h"
1920
#include "subspace/mem/addressof.h"
2021
#include "subspace/mem/forward.h"
@@ -31,110 +32,22 @@ class FnMutRef;
3132
template <class R, class... Args>
3233
class FnRef;
3334

34-
// TODO: Consider generic lambdas, it should be possible to bind them into
35-
// FnOnceRef/FnMutRef/FnRef?
36-
// Example:
37-
// ```
38-
// auto even = [](const auto& i) { return i % 2 == 0; };
39-
// auto r0 = sus::Array<int, 11>::with_values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
40-
// 10);
41-
// auto result = r0.iter().filter(even);
42-
// ```
43-
44-
namespace __private {
45-
46-
union Storage {
47-
void (*fnptr)();
48-
void* object;
49-
};
50-
51-
template <class F>
52-
struct Invoker {
53-
template <class R, class... Args>
54-
static R fnptr_call_mut(const union Storage& s, Args... args) {
55-
F f = reinterpret_cast<F>(s.fnptr);
56-
return (*f)(::sus::forward<Args>(args)...);
57-
}
58-
59-
template <class R, class... Args>
60-
static R object_call_mut(const union Storage& s, Args... args) {
61-
F& f = *static_cast<F*>(s.object);
62-
return f(::sus::forward<Args>(args)...);
63-
}
64-
65-
template <class R, class... Args>
66-
static R object_call_once(const union Storage& s, Args... args) {
67-
F& f = *static_cast<F*>(s.object);
68-
return ::sus::move(f)(::sus::forward<Args>(args)...);
69-
}
70-
71-
template <class R, class... Args>
72-
static R fnptr_call_const(const union Storage& s, Args... args) {
73-
const F f = reinterpret_cast<const F>(s.fnptr);
74-
return (*f)(::sus::forward<Args>(args)...);
75-
}
76-
77-
template <class R, class... Args>
78-
static R object_call_const(const union Storage& s, Args... args) {
79-
const F& f = *static_cast<const F*>(s.object);
80-
return f(::sus::forward<Args>(args)...);
81-
}
82-
};
83-
84-
template <class R, class... CallArgs>
85-
using InvokeFnPtr = R (*)(const union Storage& s, CallArgs... args);
86-
87-
template <class F, class R, class... Args>
88-
concept FunctionPointer =
89-
std::is_pointer_v<std::decay_t<F>> && requires(F f, Args... args) {
90-
{ (*f)(args...) } -> std::convertible_to<R>;
91-
};
92-
93-
template <class T>
94-
concept IsFunctionPointer =
95-
std::is_pointer_v<T> && std::is_function_v<std::remove_pointer_t<T>>;
96-
97-
template <class F>
98-
concept ConvertsToFunctionPointer = requires(F f) {
99-
{ +f } -> IsFunctionPointer;
100-
};
101-
102-
template <class F, class R, class... Args>
103-
concept CallableOnceMut =
104-
!FunctionPointer<F, R, Args...> && requires(F && f, Args... args) {
105-
{ ::sus::move(f)(args...) } -> std::convertible_to<R>;
106-
};
107-
108-
template <class F, class R, class... Args>
109-
concept CallableMut =
110-
!FunctionPointer<F, R, Args...> && requires(F & f, Args... args) {
111-
{ f(args...) } -> std::convertible_to<R>;
112-
};
113-
114-
template <class F, class R, class... Args>
115-
concept CallableConst =
116-
!FunctionPointer<F, R, Args...> && requires(const F& f, Args... args) {
117-
{ f(args...) } -> std::convertible_to<R>;
118-
};
119-
120-
} // namespace __private
121-
12235
/// A closure that erases the type of the internal callable object (lambda). A
12336
/// FnMutRef may be called multiple times, and holds a const callable object, so
12437
/// it will return the same value each call with the same inputs.
12538
///
126-
/// FnRef can be used as a FnMutRef, which can be used as a FnOnceRef. Lambdas can be
127-
/// converted into a FnOnceRef, FnMutRef, or FnRef directly.
39+
/// FnRef can be used as a FnMutRef, which can be used as a FnOnceRef. Lambdas
40+
/// can be converted into a FnOnceRef, FnMutRef, or FnRef directly.
12841
///
129-
/// FnOnceRef, FnMutRef and FnRef are only safe to appear as lvalues when they are a
130-
/// function parameter, and a clang-tidy check is provided to enforce this. They
131-
/// only hold a reference to the underlying lambda so they must not outlive the
132-
/// lambda.
42+
/// FnOnceRef, FnMutRef and FnRef are only safe to appear as lvalues when they
43+
/// are a function parameter, and a clang-tidy check is provided to enforce
44+
/// this. They only hold a reference to the underlying lambda so they must not
45+
/// outlive the lambda.
13346
///
13447
/// # Why can a "const" FnRef convert to a mutable FnMutRef or FnOnceRef?
13548
///
136-
/// A FnMutRef or FnOnceRef is _allowed_ to mutate its storage, but a "const" FnRef
137-
/// closure would just choose not to do so.
49+
/// A FnMutRef or FnOnceRef is _allowed_ to mutate its storage, but a "const"
50+
/// FnRef closure would just choose not to do so.
13851
///
13952
/// However, a `const FnRef` requires that the storage is not mutated, so it is
14053
/// not useful if converted to a `const FnMutRef` or `const FnOnceRef` which are
@@ -218,9 +131,9 @@ class [[sus_trivial_abi]] FnRef<R(CallArgs...)> {
218131

219132
/// `sus::construct::From` trait implementation.
220133
///
221-
/// FnRef satisfies `From<T>` for the same types that it is constructible from:
222-
/// function pointers that exactly match its own signature, and const-callable
223-
/// objects (lambdas) that are compatible with its signature.
134+
/// FnRef satisfies `From<T>` for the same types that it is constructible
135+
/// from: function pointers that exactly match its own signature, and
136+
/// const-callable objects (lambdas) that are compatible with its signature.
224137
constexpr static auto from(
225138
__private::FunctionPointer<R, CallArgs...> auto ptr) noexcept {
226139
return FnRef(ptr);
@@ -237,10 +150,12 @@ class [[sus_trivial_abi]] FnRef<R(CallArgs...)> {
237150
friend class FnMutRef;
238151

239152
constexpr FnRef(union __private::Storage storage,
240-
__private::InvokeFnPtr<R, CallArgs...> invoke)
153+
__private::InvokeFnPtr<R, CallArgs...> invoke)
241154
: storage_(storage), invoke_(invoke) {}
242155

243156
union __private::Storage storage_;
157+
/// The `invoke_` pointer is set to null to indicate the FnRef is moved-from.
158+
/// It uses another pointer value as its never-value.
244159
__private::InvokeFnPtr<R, CallArgs...> invoke_;
245160

246161
// A function pointer to use as a never-value for InvokeFnPointer.
@@ -261,18 +176,18 @@ class [[sus_trivial_abi]] FnRef<R(CallArgs...)> {
261176
/// may mutate internal state. A FnMutRef may be called multiple times, and may
262177
/// return a different value on each call with the same inputs.
263178
///
264-
/// FnRef can be used as a FnMutRef, which can be used as a FnOnceRef. Lambdas can be
265-
/// converted into a FnOnceRef, FnMutRef, or FnRef directly.
179+
/// FnRef can be used as a FnMutRef, which can be used as a FnOnceRef. Lambdas
180+
/// can be converted into a FnOnceRef, FnMutRef, or FnRef directly.
266181
///
267-
/// FnOnceRef, FnMutRef and FnRef are only safe to appear as lvalues when they are a
268-
/// function parameter, and a clang-tidy check is provided to enforce this. They
269-
/// only hold a reference to the underlying lambda so they must not outlive the
270-
/// lambda.
182+
/// FnOnceRef, FnMutRef and FnRef are only safe to appear as lvalues when they
183+
/// are a function parameter, and a clang-tidy check is provided to enforce
184+
/// this. They only hold a reference to the underlying lambda so they must not
185+
/// outlive the lambda.
271186
///
272187
/// # Why can a "const" FnRef convert to a mutable FnMutRef or FnOnceRef?
273188
///
274-
/// A FnMutRef or FnOnceRef is _allowed_ to mutate its storage, but a "const" FnRef
275-
/// closure would just choose not to do so.
189+
/// A FnMutRef or FnOnceRef is _allowed_ to mutate its storage, but a "const"
190+
/// FnRef closure would just choose not to do so.
276191
///
277192
/// However, a `const FnRef` requires that the storage is not mutated, so it is
278193
/// not useful if converted to a `const FnMutRef` or `const FnOnceRef` which are
@@ -384,10 +299,12 @@ class [[sus_trivial_abi]] FnMutRef<R(CallArgs...)> {
384299
friend class FnOnceRef;
385300

386301
constexpr FnMutRef(union __private::Storage storage,
387-
__private::InvokeFnPtr<R, CallArgs...> invoke)
302+
__private::InvokeFnPtr<R, CallArgs...> invoke)
388303
: storage_(storage), invoke_(invoke) {}
389304

390305
union __private::Storage storage_;
306+
/// The `invoke_` pointer is set to null to indicate the `FnMutRef` is
307+
/// moved-from. It uses another pointer value as its never-value.
391308
__private::InvokeFnPtr<R, CallArgs...> invoke_;
392309

393310
// A function pointer to use as a never-value for InvokeFnPointer.
@@ -407,18 +324,18 @@ class [[sus_trivial_abi]] FnMutRef<R(CallArgs...)> {
407324
/// A closure that erases the type of the internal callable object (lambda). A
408325
/// FnOnceRef may only be called a single time.
409326
///
410-
/// FnRef can be used as a FnMutRef, which can be used as a FnOnceRef. Lambdas can be
411-
/// converted into a FnOnceRef, FnMutRef, or FnRef directly.
327+
/// FnRef can be used as a FnMutRef, which can be used as a FnOnceRef. Lambdas
328+
/// can be converted into a FnOnceRef, FnMutRef, or FnRef directly.
412329
///
413-
/// FnOnceRef, FnMutRef and FnRef are only safe to appear as lvalues when they are a
414-
/// function parameter, and a clang-tidy check is provided to enforce this. They
415-
/// only hold a reference to the underlying lambda so they must not outlive the
416-
/// lambda.
330+
/// FnOnceRef, FnMutRef and FnRef are only safe to appear as lvalues when they
331+
/// are a function parameter, and a clang-tidy check is provided to enforce
332+
/// this. They only hold a reference to the underlying lambda so they must not
333+
/// outlive the lambda.
417334
///
418335
/// # Why can a "const" FnRef convert to a mutable FnMutRef or FnOnceRef?
419336
///
420-
/// A FnMutRef or FnOnceRef is _allowed_ to mutate its storage, but a "const" FnRef
421-
/// closure would just choose not to do so.
337+
/// A FnMutRef or FnOnceRef is _allowed_ to mutate its storage, but a "const"
338+
/// FnRef closure would just choose not to do so.
422339
///
423340
/// However, a `const FnRef` requires that the storage is not mutated, so it is
424341
/// not useful if converted to a `const FnMutRef` or `const FnOnceRef` which are
@@ -465,9 +382,9 @@ class [[sus_trivial_abi]] FnOnceRef<R(CallArgs...)> {
465382

466383
/// Construction from FnMutRef.
467384
///
468-
/// Since FnMutRef is callable, FnOnceRef is already constructible from it, but
469-
/// this constructor avoids extra indirections being inserted when converting,
470-
/// since otherwise an extra invoker call would be introduced.
385+
/// Since FnMutRef is callable, FnOnceRef is already constructible from it,
386+
/// but this constructor avoids extra indirections being inserted when
387+
/// converting, since otherwise an extra invoker call would be introduced.
471388
constexpr FnOnceRef(FnMutRef<R(CallArgs...)>&& o sus_lifetimebound) noexcept
472389
: storage_(o.storage_),
473390
invoke_(::sus::mem::replace_ptr(o.invoke_, nullptr)) {
@@ -574,6 +491,8 @@ class [[sus_trivial_abi]] FnOnceRef<R(CallArgs...)> {
574491
: storage_(storage), invoke_(invoke) {}
575492

576493
union __private::Storage storage_;
494+
/// The `invoke_` pointer is set to null to indicate the FnOnceRef is
495+
/// moved-from. It uses another pointer value as its never-value.
577496
__private::InvokeFnPtr<R, CallArgs...> invoke_;
578497

579498
// A function pointer to use as a never-value for InvokeFnPointer.

0 commit comments

Comments
 (0)