Skip to content

Commit 598f6b3

Browse files
committed
Document all of Fn concepts and their helpers
Move the helpers into a __private/ folder header.
1 parent 1ea04a0 commit 598f6b3

File tree

3 files changed

+163
-80
lines changed

3 files changed

+163
-80
lines changed

subspace/fn/__private/signature.h

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// Copyright 2022 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+
19+
#include "subspace/mem/move.h"
20+
21+
namespace sus::fn {
22+
struct Anything;
23+
}
24+
25+
namespace sus::fn::__private {
26+
27+
/// The return type inferred for a functor when it is not able to be called with
28+
/// a set of argument types, which indicates an error occured.
29+
struct NoOverloadMatchesArguments {};
30+
31+
/// Always fails, used to generate a static_assert() when a type is meant to be
32+
/// a function signature but it's ill-formed.
33+
template <class T>
34+
concept InvalidFunctionSignature = false;
35+
36+
/// Represents the argument types that will be passed to a functor.
37+
template <class... Ts>
38+
struct ArgsPack;
39+
40+
/// Unpacks a function signature `ReturnType(Args...)` into its components.
41+
///
42+
/// Generates a static_assert() failure if the `T` in `Sig<T>` is not a function
43+
/// signature.
44+
template <class R, class... A>
45+
struct Sig {
46+
static_assert(
47+
InvalidFunctionSignature<R>,
48+
"expected a function signature of the form `ReturnType(Args...)`");
49+
};
50+
51+
template <class R, class... A>
52+
struct Sig<R(A...)> {
53+
using Return = R;
54+
using Args = ArgsPack<A...>;
55+
};
56+
57+
/// Unpacks an `ArgsPack` of function argument types, and determines if a
58+
/// functor `F` is once-callable with them, to be called through a `FnOnce`.
59+
///
60+
/// If `F` is callable with the argument types in the `ArgsPack`, the
61+
/// `returns()` method will have the same return type as `F`. Otherwise, it
62+
/// indicates failure by having a return type of `NoOverloadMatchesArguments`.
63+
template <class F, class ArgsPack>
64+
struct InvokedFnOnce {
65+
static NoOverloadMatchesArguments returns();
66+
};
67+
68+
template <class F, class... Ts>
69+
requires requires(F&& f) {
70+
{ ::sus::move(f)(std::declval<Ts>()...) };
71+
}
72+
struct InvokedFnOnce<F, ArgsPack<Ts...>> {
73+
static decltype(std::declval<F&&>()(std::declval<Ts>()...)) returns();
74+
};
75+
76+
/// Unpacks an `ArgsPack` of function argument types, and determines if a
77+
/// functor `F` is mutably-callable with them, to be called through a `FnMut`.
78+
///
79+
/// If `F` is callable with the argument types in the `ArgsPack`, the
80+
/// `returns()` method will have the same return type as `F`. Otherwise, it
81+
/// indicates failure by having a return type of `NoOverloadMatchesArguments`.
82+
template <class F, class ArgsPack>
83+
struct InvokedFnMut {
84+
static NoOverloadMatchesArguments returns();
85+
};
86+
87+
template <class F, class... Ts>
88+
requires requires(F& f) {
89+
{ f(std::declval<Ts>()...) };
90+
}
91+
struct InvokedFnMut<F, ArgsPack<Ts...>> {
92+
static decltype(std::declval<F&>()(std::declval<Ts>()...)) returns();
93+
};
94+
95+
/// Unpacks an `ArgsPack` of function argument types, and determines if a
96+
/// functor `F` is const-callable with them, to be called through a `Fn`.
97+
///
98+
/// If `F` is callable with the argument types in the `ArgsPack`, the
99+
/// `returns()` method will have the same return type as `F`. Otherwise, it
100+
/// indicates failure by having a return type of `NoOverloadMatchesArguments`.
101+
template <class F, class ArgsPack>
102+
struct InvokedFn {
103+
static NoOverloadMatchesArguments returns();
104+
};
105+
106+
template <class F, class... Ts>
107+
requires requires(const F& f) {
108+
{ f(std::declval<Ts>()...) };
109+
}
110+
struct InvokedFn<F, ArgsPack<Ts...>> {
111+
static decltype(std::declval<const F&>()(std::declval<Ts>()...)) returns();
112+
};
113+
114+
/// Whether the `ReturnType` of a functor is compatible with receiving it as
115+
/// `T`.
116+
///
117+
/// If the receiver specifies `T` as `sus::fn::Anything` then all return types
118+
/// are accepted, which can be useful in generic code. Simiarly, if the receiver
119+
/// specifies `T` as `sus::fn::NonVoid` then all return types other than `void`
120+
/// are accepted.
121+
template <class ReturnType, class T>
122+
concept ValidReturnType =
123+
!std::same_as<ReturnType, NoOverloadMatchesArguments> &&
124+
(std::same_as<::sus::fn::Anything, T> ||
125+
std::convertible_to<ReturnType, T>);
126+
127+
} // namespace sus::fn::__private

subspace/fn/fn.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@
1717
#include "subspace/fn/bind.h"
1818
#include "subspace/fn/fn_box_defn.h"
1919
#include "subspace/fn/fn_box_impl.h"
20-
#include "subspace/fn/fn_ref.h"
2120
#include "subspace/fn/fn_concepts.h"
21+
#include "subspace/fn/fn_ref.h"
22+
#include "subspace/fn/run_fn.h"

subspace/fn/fn_concepts.h

Lines changed: 34 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -14,87 +14,27 @@
1414

1515
#pragma once
1616

17-
#include <concepts>
18-
19-
#include "subspace/mem/forward.h"
17+
#include "subspace/fn/__private/signature.h"
2018
#include "subspace/mem/move.h"
2119

2220
namespace sus::fn {
2321

22+
/// When used as the return type of the function signature in `Fn`, `FnMut` and
23+
/// `FnOnce`, the concepts will match against any return type from a functor
24+
/// except `void`.
2425
struct NonVoid {
2526
template <class T>
2627
constexpr NonVoid(T&&) noexcept {}
2728
};
2829

30+
/// When used as the return type of the function signature in `Fn`, `FnMut` and
31+
/// `FnOnce`, the concepts will match against any return type from a functor
32+
/// including `void`.
2933
struct Anything {
3034
template <class T>
3135
constexpr Anything(T&&) noexcept {}
3236
};
3337

34-
namespace __private {
35-
template <class... Ts>
36-
struct Pack;
37-
38-
template <class R, class... Args>
39-
struct Sig;
40-
41-
template <class R, class... A>
42-
struct Sig<R(A...)> {
43-
using Return = R;
44-
using Args = Pack<A...>;
45-
};
46-
47-
struct NoOverloadMatchesArguments {};
48-
49-
template <class F, class ArgsPack>
50-
struct InvokedFnOnce {
51-
constexpr static NoOverloadMatchesArguments return_type();
52-
};
53-
54-
template <class F, class... Ts>
55-
requires requires(F&& f) {
56-
{ ::sus::move(f)(std::declval<Ts>()...) };
57-
}
58-
struct InvokedFnOnce<F, Pack<Ts...>> {
59-
constexpr static decltype(std::declval<F&&>()(std::declval<Ts>()...))
60-
return_type();
61-
};
62-
63-
template <class F, class ArgsPack>
64-
struct InvokedFnMut {
65-
constexpr static NoOverloadMatchesArguments return_type();
66-
};
67-
68-
template <class F, class... Ts>
69-
requires requires(F& f) {
70-
{ f(std::declval<Ts>()...) };
71-
}
72-
struct InvokedFnMut<F, Pack<Ts...>> {
73-
constexpr static decltype(std::declval<F&>()(std::declval<Ts>()...))
74-
return_type();
75-
};
76-
77-
template <class F, class ArgsPack>
78-
struct InvokedFn {
79-
constexpr static NoOverloadMatchesArguments return_type();
80-
};
81-
82-
template <class F, class... Ts>
83-
requires requires(const F& f) {
84-
{ f(std::declval<Ts>()...) };
85-
}
86-
struct InvokedFn<F, Pack<Ts...>> {
87-
constexpr static decltype(std::declval<const F&>()(std::declval<Ts>()...))
88-
return_type();
89-
};
90-
91-
template <class ReturnType, class T>
92-
concept ValidReturnType =
93-
!std::same_as<ReturnType, NoOverloadMatchesArguments> &&
94-
(std::same_as<::sus::fn::Anything, T> ||
95-
std::convertible_to<ReturnType, T>);
96-
} // namespace __private
97-
9838
/// The version of a callable object that is called on an rvalue (moved-from)
9939
/// receiver. A `FnOnce` is typically the best fit for any callable that will
10040
/// only be called at most once. However when a template (or constexpr) is not
@@ -116,6 +56,8 @@ concept ValidReturnType =
11656
/// ```
11757
///
11858
/// # Use of `FnOnce`
59+
/// The `sus::run_once()` helper ensures that FnOnce is called correctly.
60+
///
11961
/// A `FnOnce` should only be called once, and should be moved with
12062
/// `sus::move()` when calling it. It is typically received as an rvalue
12163
/// reference to avoid an unnecessary copy or move operation.
@@ -153,11 +95,14 @@ concept ValidReturnType =
15395
/// });
15496
/// sus::check(x == 400 + 4);
15597
/// ```
156-
template <class F, class S>
98+
template <class F, class... S>
15799
concept FnOnce = requires(F&& f) {
100+
// Receives and passes along the signature as a pack instead of a single
101+
// argument in order to consistently provide a static_assert() in `Sig` when
102+
// `S` is not a function signature.
158103
{
159-
__private::InvokedFnOnce<F, typename __private::Sig<S>::Args>::return_type()
160-
} -> __private::ValidReturnType<typename __private::Sig<S>::Return>;
104+
__private::InvokedFnOnce<F, typename __private::Sig<S...>::Args>::returns()
105+
} -> __private::ValidReturnType<typename __private::Sig<S...>::Return>;
161106
};
162107

163108
/// The version of a callable object that is allowed to mutate internal state
@@ -185,6 +130,8 @@ concept FnOnce = requires(F&& f) {
185130
/// ```
186131
///
187132
/// # Use of `FnMut`
133+
/// The `sus::run_mut()` helper ensures that `FnMut` is called correctly.
134+
///
188135
/// A `FnMut` may be called any number of times, unlike `FnOnce`, and need not
189136
/// be moved when called. It is typically received as a function parameter by
190137
/// value, which isolates any internal mutation to the current function.
@@ -219,13 +166,16 @@ concept FnOnce = requires(F&& f) {
219166
/// });
220167
/// sus::check(x == 401 + 102);
221168
/// ```
222-
template <class F, class S>
169+
template <class F, class... S>
223170
concept FnMut = requires(F& f) {
171+
// Receives and passes along the signature as a pack instead of a single
172+
// argument in order to consistently provide a static_assert() in `Sig` when
173+
// `S` is not a function signature.
224174
{
225-
__private::InvokedFnMut<F, typename __private::Sig<S>::Args>::return_type()
226-
} -> __private::ValidReturnType<typename __private::Sig<S>::Return>;
175+
__private::InvokedFnMut<F, typename __private::Sig<S...>::Args>::returns()
176+
} -> __private::ValidReturnType<typename __private::Sig<S...>::Return>;
227177

228-
requires FnOnce<F, S>;
178+
requires FnOnce<F, S...>;
229179
};
230180

231181
/// The version of a callable object that may be called multiple times without
@@ -253,6 +203,8 @@ concept FnMut = requires(F& f) {
253203
/// ```
254204
///
255205
/// # Use of `Fn`
206+
/// The `sus::run()` helper ensures that `Fn` is called correctly.
207+
///
256208
/// A `Fn` may be called any number of times, unlike `FnOnce`, and need not
257209
/// be moved when called. It is typically received as a function parameter as a
258210
/// const reference, which ensures a non-mutating call operator will be used.
@@ -286,14 +238,17 @@ concept FnMut = requires(F& f) {
286238
/// });
287239
/// sus::check(x == 401 + 101);
288240
/// ```
289-
template <class F, class S>
241+
template <class F, class... S>
290242
concept Fn = requires(const F& f) {
243+
// Receives and passes along the signature as a pack instead of a single
244+
// argument in order to consistently provide a static_assert() in `Sig` when
245+
// `S` is not a function signature.
291246
{
292-
__private::InvokedFn<F, typename __private::Sig<S>::Args>::return_type()
293-
} -> __private::ValidReturnType<typename __private::Sig<S>::Return>;
247+
__private::InvokedFn<F, typename __private::Sig<S...>::Args>::returns()
248+
} -> __private::ValidReturnType<typename __private::Sig<S...>::Return>;
294249

295-
requires FnMut<F, S>;
296-
requires FnOnce<F, S>;
250+
requires FnMut<F, S...>;
251+
requires FnOnce<F, S...>;
297252
};
298253

299254
} // namespace sus::fn

0 commit comments

Comments
 (0)