Skip to content

Commit 1ea04a0

Browse files
committed
Fn, FnMut, FnOnce concepts take a signature
Instead of Fn<Return, Arg1, Arg2> you now write Fn<Return(Arg1, Arg2)> And when it is passed something that can't be called with (Arg1, Arg2) the failing includes the type 'NoOverloadMatchesArguments' to indicate the failure. If the return type doesn't convert to Return, the usual and best concept error message is reported where the resolve return type is compared against the desired one.
1 parent 809465c commit 1ea04a0

File tree

3 files changed

+243
-111
lines changed

3 files changed

+243
-111
lines changed

subspace/fn/fn_concepts.h

Lines changed: 108 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,68 @@ struct Anything {
3232
};
3333

3434
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+
3591
template <class ReturnType, class T>
36-
concept CorrectReturn =
37-
std::same_as<::sus::fn::Anything, T> || std::convertible_to<ReturnType, T>;
38-
}
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
3997

4098
/// The version of a callable object that is called on an rvalue (moved-from)
4199
/// receiver. A `FnOnce` is typically the best fit for any callable that will
@@ -49,6 +107,14 @@ concept CorrectReturn =
49107
/// is not `&`-qualified). Mutable and const lambdas will satisfy
50108
/// `FnOnce`.
51109
///
110+
/// The second argument of `FnOnce<F, S>` is a function signature with the
111+
/// format `ReturnType(Args...)`, where `Args...` are the arguments that will
112+
/// be passed to the `FnOnce` and `ReturnType` is what is expected to be
113+
/// received back. It would appear as a matching concept as:
114+
/// ```
115+
/// void function(FnOnce<ReturnType(Args...)> auto&& f) { ... }
116+
/// ```
117+
///
52118
/// # Use of `FnOnce`
53119
/// A `FnOnce` should only be called once, and should be moved with
54120
/// `sus::move()` when calling it. It is typically received as an rvalue
@@ -78,7 +144,7 @@ concept CorrectReturn =
78144
/// ```
79145
/// // Accepts any type that can be called once with (Option<i32>) and returns
80146
/// // i32.
81-
/// i32 call_once(sus::fn::FnOnce<i32, sus::Option<i32>> auto&& f) {
147+
/// i32 call_once(sus::fn::FnOnce<i32(sus::Option<i32>)> auto&& f) {
82148
/// return sus::move(f)(sus::some(400)); // Returns an i32.
83149
/// }
84150
///
@@ -87,11 +153,11 @@ concept CorrectReturn =
87153
/// });
88154
/// sus::check(x == 400 + 4);
89155
/// ```
90-
template <class F, class R, class... Args>
91-
concept FnOnce = requires(F&& f, Args... args) {
156+
template <class F, class S>
157+
concept FnOnce = requires(F&& f) {
92158
{
93-
::sus::move(f)(::sus::forward<Args>(args)...)
94-
} -> __private::CorrectReturn<R>;
159+
__private::InvokedFnOnce<F, typename __private::Sig<S>::Args>::return_type()
160+
} -> __private::ValidReturnType<typename __private::Sig<S>::Return>;
95161
};
96162

97163
/// The version of a callable object that is allowed to mutate internal state
@@ -110,6 +176,14 @@ concept FnOnce = requires(F&& f, Args... args) {
110176
/// is not `&&`-qualified). Mutable and const lambdas will satisfy
111177
/// `FnMut`.
112178
///
179+
/// The second argument of `FnMut<F, S>` is a function signature with the
180+
/// format `ReturnType(Args...)`, where `Args...` are the arguments that will
181+
/// be passed to the `FnMut` and `ReturnType` is what is expected to be
182+
/// received back. It would appear as a matching concept as:
183+
/// ```
184+
/// void function(FnMut<ReturnType(Args...)> auto f) { ... }
185+
/// ```
186+
///
113187
/// # Use of `FnMut`
114188
/// A `FnMut` may be called any number of times, unlike `FnOnce`, and need not
115189
/// be moved when called. It is typically received as a function parameter by
@@ -135,7 +209,7 @@ concept FnOnce = requires(F&& f, Args... args) {
135209
/// ```
136210
/// // Accepts any type that can be called once with (Option<i32>) and returns
137211
/// // i32.
138-
/// static i32 call_mut(sus::fn::FnMut<i32, sus::Option<i32>> auto f) {
212+
/// static i32 call_mut(sus::fn::FnMut<i32(sus::Option<i32>)> auto f) {
139213
/// return f(sus::some(400)) + f(sus::some(100)); // Returns an i32.
140214
/// }
141215
///
@@ -145,10 +219,13 @@ concept FnOnce = requires(F&& f, Args... args) {
145219
/// });
146220
/// sus::check(x == 401 + 102);
147221
/// ```
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...>;
222+
template <class F, class S>
223+
concept FnMut = requires(F& f) {
224+
{
225+
__private::InvokedFnMut<F, typename __private::Sig<S>::Args>::return_type()
226+
} -> __private::ValidReturnType<typename __private::Sig<S>::Return>;
227+
228+
requires FnOnce<F, S>;
152229
};
153230

154231
/// The version of a callable object that may be called multiple times without
@@ -167,6 +244,14 @@ concept FnMut = requires(F& f, Args... args) {
167244
/// is not `&&`-qualified). Mutable and const lambdas will satisfy
168245
/// `FnMut`.
169246
///
247+
/// The second argument of `Fn<F, S>` is a function signature with the format
248+
/// `ReturnType(Args...)`, where `Args...` are the arguments that will be passed
249+
/// to the `Fn` and `ReturnType` is what is expected to be received back. It
250+
/// would appear as a matching concept as:
251+
/// ```
252+
/// void function(const Fn<ReturnType(Args...)> auto& f) { ... }
253+
/// ```
254+
///
170255
/// # Use of `Fn`
171256
/// A `Fn` may be called any number of times, unlike `FnOnce`, and need not
172257
/// be moved when called. It is typically received as a function parameter as a
@@ -192,20 +277,23 @@ concept FnMut = requires(F& f, Args... args) {
192277
/// ```
193278
/// // Accepts any type that can be called once with (Option<i32>) and returns
194279
/// // i32.
195-
/// static i32 call_fn(const sus::fn::Fn<i32, sus::Option<i32>> auto& f) {
280+
/// static i32 call_fn(const sus::fn::Fn<i32(sus::Option<i32>)> auto& f) {
196281
/// return f(sus::some(400)) + f(sus::some(100)); // Returns an i32.
197282
/// }
198-
///
283+
///
199284
/// i32 x = call_fn([i = 1_i32](sus::Option<i32> o) -> i32 {
200285
/// return sus::move(o).unwrap_or_default() + i;
201286
/// });
202287
/// sus::check(x == 401 + 101);
203288
/// ```
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...>;
289+
template <class F, class S>
290+
concept Fn = requires(const F& f) {
291+
{
292+
__private::InvokedFn<F, typename __private::Sig<S>::Args>::return_type()
293+
} -> __private::ValidReturnType<typename __private::Sig<S>::Return>;
294+
295+
requires FnMut<F, S>;
296+
requires FnOnce<F, S>;
209297
};
210298

211299
} // namespace sus::fn

0 commit comments

Comments
 (0)