Skip to content

Commit 809465c

Browse files
committed
Add Fn, FnMut, FnOnce as concepts
1 parent b526a2d commit 809465c

File tree

5 files changed

+429
-13
lines changed

5 files changed

+429
-13
lines changed

subspace/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ add_executable(subspace_unittests
153153
"construct/into_unittest.cc"
154154
"construct/default_unittest.cc"
155155
"fn/fn_box_unittest.cc"
156+
"fn/fn_concepts_unittest.cc"
156157
"fn/fn_ref_unittest.cc"
157158
"iter/iterator_unittest.cc"
158159
"mem/addressof_unittest.cc"

subspace/fn/fn.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@
1818
#include "subspace/fn/fn_box_defn.h"
1919
#include "subspace/fn/fn_box_impl.h"
2020
#include "subspace/fn/fn_ref.h"
21+
#include "subspace/fn/fn_concepts.h"

subspace/fn/fn_concepts.h

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
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+
19+
#include "subspace/mem/forward.h"
20+
#include "subspace/mem/move.h"
21+
22+
namespace sus::fn {
23+
24+
struct NonVoid {
25+
template <class T>
26+
constexpr NonVoid(T&&) noexcept {}
27+
};
28+
29+
struct Anything {
30+
template <class T>
31+
constexpr Anything(T&&) noexcept {}
32+
};
33+
34+
namespace __private {
35+
template <class ReturnType, class T>
36+
concept CorrectReturn =
37+
std::same_as<::sus::fn::Anything, T> || std::convertible_to<ReturnType, T>;
38+
}
39+
40+
/// The version of a callable object that is called on an rvalue (moved-from)
41+
/// receiver. A `FnOnce` is typically the best fit for any callable that will
42+
/// only be called at most once. However when a template (or constexpr) is not
43+
/// required, receiving a `FnOnceRef` instead of `FnOnce auto&&` will typically
44+
/// produce less code by using type erasure to avoid template instantiation.
45+
///
46+
/// A type that satisfies `FnOnce` will return a type that can be converted to
47+
/// `R` when called with the arguments `Args...`. `FnOnce` is satisfied by
48+
/// being callable as an rvalue (which is done by providing an operator() that
49+
/// is not `&`-qualified). Mutable and const lambdas will satisfy
50+
/// `FnOnce`.
51+
///
52+
/// # Use of `FnOnce`
53+
/// A `FnOnce` should only be called once, and should be moved with
54+
/// `sus::move()` when calling it. It is typically received as an rvalue
55+
/// reference to avoid an unnecessary copy or move operation.
56+
///
57+
/// Calling it multiple times may panic or cause Undefined Behaviour. Not moving
58+
/// the `FnOnce` when calling it may fail to compile, panic, or cause Undefined
59+
/// Behaviour depending on the type that is being used to satisfy `FnOnce`.
60+
///
61+
/// # Compatibility
62+
/// Any callable type that satisfies `Fn` or `FnMut` will also satisfy `FnOnce`.
63+
/// While a `FnOnce` should only be called once, this is compatible with the
64+
/// requirements of `FnMut` and `Fn` which can be called only a single time. As
65+
/// well, `FnOnce` is allowed to mutate internal state, but it does not have to,
66+
/// which is compatible with the const nature of `Fn`.
67+
///
68+
/// # Subspace types that satisfy `FnOnce`
69+
/// The `FnOnceRef` and `FnOnceBox` types in the Subspace library satisfy
70+
/// `FnOnce`. They must be moved from when called, and they panic if called more
71+
/// than once, as that implies a use-after-move.
72+
///
73+
/// Like the concepts, `FnRef` is convertible to `FnMutRef` is convertible to
74+
/// `FnOnceRef`. And `FnBox` is convertible to `FnMutBox` is convertible to
75+
/// `FnOnceBox`.
76+
///
77+
/// # Example
78+
/// ```
79+
/// // Accepts any type that can be called once with (Option<i32>) and returns
80+
/// // i32.
81+
/// i32 call_once(sus::fn::FnOnce<i32, sus::Option<i32>> auto&& f) {
82+
/// return sus::move(f)(sus::some(400)); // Returns an i32.
83+
/// }
84+
///
85+
/// i32 x = call_once([](sus::Option<i32> o) -> i32 {
86+
/// return sus::move(o).unwrap_or_default() + 4;
87+
/// });
88+
/// sus::check(x == 400 + 4);
89+
/// ```
90+
template <class F, class R, class... Args>
91+
concept FnOnce = requires(F&& f, Args... args) {
92+
{
93+
::sus::move(f)(::sus::forward<Args>(args)...)
94+
} -> __private::CorrectReturn<R>;
95+
};
96+
97+
/// The version of a callable object that is allowed to mutate internal state
98+
/// and may be called multiple times. A `FnMut` is typically the best fit for
99+
/// any callable that may be called one or more times. However when a template
100+
/// (or constexpr) is not required, receiving a `FnMutRef` instead of `FnMut
101+
/// auto` will typically produce less code by using type erasure to avoid
102+
/// template instantiation.
103+
///
104+
/// Because a `FnMut` is able to mutate internal state, it may return different
105+
/// values each time it is called with the same inputs.
106+
///
107+
/// A type that satisfies `FnMut` will return a type that can be converted to
108+
/// `R` when called with the arguments `Args...`. `FnMut` is satisfied by
109+
/// being callable as an lvalue (which is done by providing an operator() that
110+
/// is not `&&`-qualified). Mutable and const lambdas will satisfy
111+
/// `FnMut`.
112+
///
113+
/// # Use of `FnMut`
114+
/// A `FnMut` may be called any number of times, unlike `FnOnce`, and need not
115+
/// be moved when called. It is typically received as a function parameter by
116+
/// value, which isolates any internal mutation to the current function.
117+
///
118+
/// # Compatibility
119+
/// Any callable type that satisfies `Fn` or `FnMut` will also satisfy `FnOnce`.
120+
/// While a `FnOnce` should only be called once, this is compatible with the
121+
/// requirements of `FnMut` and `Fn` which can be called only a single time. As
122+
/// well, `FnOnce` is allowed to mutate internal state, but it does not have to,
123+
/// which is compatible with the const nature of `Fn`.
124+
///
125+
/// # Subspace types that satisfy `FnMut`
126+
/// The `FnMutRef` and `FnMutBox` types in the Subspace library satisfy `FnMut`
127+
/// (and thus `FnOnce` as well). They may be called any number of times and are
128+
/// able to mutate internal state.
129+
///
130+
/// Like the concepts, `FnRef` is convertible to `FnMutRef` is convertible to
131+
/// `FnOnceRef`. And `FnBox` is convertible to `FnMutBox` is convertible to
132+
/// `FnOnceBox`.
133+
///
134+
/// # Example
135+
/// ```
136+
/// // Accepts any type that can be called once with (Option<i32>) and returns
137+
/// // i32.
138+
/// static i32 call_mut(sus::fn::FnMut<i32, sus::Option<i32>> auto f) {
139+
/// return f(sus::some(400)) + f(sus::some(100)); // Returns an i32.
140+
/// }
141+
///
142+
/// i32 x = call_mut([i = 0_i32](sus::Option<i32> o) mutable -> i32 {
143+
/// i += 1;
144+
/// return sus::move(o).unwrap_or_default() + i;
145+
/// });
146+
/// sus::check(x == 401 + 102);
147+
/// ```
148+
template <class F, class R, class... Args>
149+
concept FnMut = requires(F& f, Args... args) {
150+
{ f(::sus::forward<Args>(args)...) } -> __private::CorrectReturn<R>;
151+
requires FnOnce<F, R, Args...>;
152+
};
153+
154+
/// The version of a callable object that may be called multiple times without
155+
/// mutating internal state. A `Fn` is useful for a callable that is received as
156+
/// a const reference to indicate it and may be called one or more times and
157+
/// does not change between call. However when a template (or constexpr) is not
158+
/// required, receiving a `FnRef` instead of `const Fn auto&` will typically
159+
/// produce less code by using type erasure to avoid template instantiation.
160+
///
161+
/// Because a `FnMut` is able to mutate internal state, it may return different
162+
/// values each time it is called with the same inputs.
163+
///
164+
/// A type that satisfies `FnMut` will return a type that can be converted to
165+
/// `R` when called with the arguments `Args...`. `FnMut` is satisfied by
166+
/// being callable as an lvalue (which is done by providing an operator() that
167+
/// is not `&&`-qualified). Mutable and const lambdas will satisfy
168+
/// `FnMut`.
169+
///
170+
/// # Use of `Fn`
171+
/// A `Fn` may be called any number of times, unlike `FnOnce`, and need not
172+
/// be moved when called. It is typically received as a function parameter as a
173+
/// const reference, which ensures a non-mutating call operator will be used.
174+
///
175+
/// # Compatibility
176+
/// Any callable type that satisfies `Fn` will also satisfy `FnMut` and
177+
/// `FnOnce`. A `Fn` may be called multiple times, or a single time, which is
178+
/// compatible with both `FnMut` and `FnOnce`. And while `FnMut` and `FnOnce`
179+
/// are able to mutate state when run, they are not required to and a constant
180+
/// `Fn` satisfies them.
181+
///
182+
/// # Subspace types that satisfy `FnMut`
183+
/// The `FnRef` and `FnBox` types in the Subspace library satisfy `Fn` (and thus
184+
/// `FnMut` and `FnOnce` as well). They may be called any number of times and
185+
/// are callable as a const object.
186+
///
187+
/// Like the concepts, `FnRef` is convertible to `FnMutRef` is convertible to
188+
/// `FnOnceRef`. And `FnBox` is convertible to `FnMutBox` is convertible to
189+
/// `FnOnceBox`.
190+
///
191+
/// # Example
192+
/// ```
193+
/// // Accepts any type that can be called once with (Option<i32>) and returns
194+
/// // i32.
195+
/// static i32 call_fn(const sus::fn::Fn<i32, sus::Option<i32>> auto& f) {
196+
/// return f(sus::some(400)) + f(sus::some(100)); // Returns an i32.
197+
/// }
198+
///
199+
/// i32 x = call_fn([i = 1_i32](sus::Option<i32> o) -> i32 {
200+
/// return sus::move(o).unwrap_or_default() + i;
201+
/// });
202+
/// sus::check(x == 401 + 101);
203+
/// ```
204+
template <class F, class R, class... Args>
205+
concept Fn = requires(const F& f, Args... args) {
206+
{ f(::sus::forward<Args>(args)...) } -> __private::CorrectReturn<R>;
207+
requires FnMut<F, R, Args...>;
208+
requires FnOnce<F, R, Args...>;
209+
};
210+
211+
} // namespace sus::fn

0 commit comments

Comments
 (0)