Skip to content

Commit fd9d04b

Browse files
committed
Add stdexec::scope_token
This diff adds a definition for `concept stdexec::scope_token` plus tests confirming it accepts and rejects the expected things.
1 parent 9327406 commit fd9d04b

File tree

3 files changed

+160
-0
lines changed

3 files changed

+160
-0
lines changed

include/stdexec/__detail/__scope_concepts.hpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,37 @@ namespace stdexec {
2929
{ static_cast<bool>(assoc) } noexcept;
3030
{ assoc.try_associate() } -> same_as<_Assoc>;
3131
};
32+
33+
namespace __scope_concepts {
34+
struct __test_sender {
35+
using sender_concept = stdexec::sender_t;
36+
37+
using completion_signatures = stdexec::completion_signatures<
38+
stdexec::set_value_t(int),
39+
stdexec::set_error_t(std::exception_ptr),
40+
stdexec::set_stopped_t()
41+
>;
42+
43+
struct __op {
44+
using operation_state_concept = stdexec::operation_state_t;
45+
46+
__op() = default;
47+
__op(__op&&) = delete;
48+
49+
void start() & noexcept {
50+
}
51+
};
52+
53+
template <class _Receiver>
54+
__op connect(_Receiver) {
55+
return {};
56+
}
57+
};
58+
} // namespace __scope_concepts
59+
60+
template <class _Token>
61+
concept scope_token = copyable<_Token> && requires(const _Token token) {
62+
{ token.try_associate() } -> scope_association;
63+
{ token.wrap(__declval<__scope_concepts::__test_sender>()) } -> sender_in<stdexec::env<>>;
64+
};
3265
} // namespace stdexec

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ set(stdexec_test_sources
3030
stdexec/concepts/test_concept_operation_state.cpp
3131
stdexec/concepts/test_concepts_sender.cpp
3232
stdexec/concepts/test_concepts_scope_association.cpp
33+
stdexec/concepts/test_concepts_scope_token.cpp
3334
stdexec/concepts/test_awaitables.cpp
3435
stdexec/algos/factories/test_just.cpp
3536
stdexec/algos/factories/test_transfer_just.cpp
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* Copyright (c) 2022 Ian Petersen
3+
*
4+
* Licensed under the Apache License Version 2.0 with LLVM Exceptions
5+
* (the "License"); you may not use this file except in compliance with
6+
* the License. You may obtain a copy of the License at
7+
*
8+
* https://llvm.org/LICENSE.txt
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include <catch2/catch.hpp>
18+
#include <stdexec/execution.hpp>
19+
20+
namespace ex = stdexec;
21+
22+
namespace {
23+
24+
TEST_CASE("Scope token helpers are correctly defined", "[concepts][scope_token]") {
25+
// check the test-sender and test-env definitions are appropriate
26+
STATIC_REQUIRE(ex::sender<ex::__scope_concepts::__test_sender>);
27+
STATIC_REQUIRE(ex::sender_in<ex::__scope_concepts::__test_sender, ex::env<>>);
28+
STATIC_REQUIRE(ex::operation_state<ex::__scope_concepts::__test_sender::__op>);
29+
}
30+
31+
// a "null" token that can always create new associations
32+
struct null_token {
33+
// the always-truthy association type
34+
struct assoc {
35+
// this need not be explicit, although it should be
36+
constexpr operator bool() const noexcept {
37+
return true;
38+
}
39+
40+
// this may throw, although it need not
41+
constexpr assoc try_associate() const noexcept {
42+
return {};
43+
}
44+
};
45+
46+
constexpr assoc try_associate() const noexcept {
47+
return {};
48+
}
49+
50+
template <ex::sender Sender>
51+
Sender&& wrap(Sender&& snd) const noexcept {
52+
return std::forward<Sender>(snd);
53+
}
54+
};
55+
56+
struct throwing_try_associate : null_token {
57+
constexpr assoc try_associate() const noexcept(false) {
58+
return {};
59+
}
60+
};
61+
62+
struct throwing_wrap : null_token {
63+
template <ex::sender Sender>
64+
constexpr Sender&& wrap(Sender&& snd) const noexcept(false) {
65+
return std::forward<Sender>(snd);
66+
}
67+
};
68+
69+
struct wrapping_wrap : null_token {
70+
template <ex::sender Sender>
71+
struct wrapper : Sender { };
72+
73+
template <ex::sender Sender>
74+
auto wrap(Sender&& snd) const noexcept {
75+
return wrapper{std::forward<Sender>(snd)};
76+
}
77+
};
78+
79+
TEST_CASE("Scope token concept accepts basic token types", "[concepts][scope_token]") {
80+
// scope_token should accept the basic null_token
81+
STATIC_REQUIRE(ex::scope_token<null_token>);
82+
83+
// it's ok for try_associate to throw
84+
STATIC_REQUIRE(ex::scope_token<throwing_try_associate>);
85+
86+
// it's ok for wrap to throw
87+
STATIC_REQUIRE(ex::scope_token<throwing_wrap>);
88+
89+
// it's ok for wrap to change the type of its argument
90+
STATIC_REQUIRE(ex::scope_token<wrapping_wrap>);
91+
}
92+
93+
struct move_only : null_token {
94+
move_only() = default;
95+
move_only(move_only&&) = default;
96+
~move_only() = default;
97+
98+
move_only& operator=(move_only&&) = default;
99+
};
100+
101+
struct non_const_try_associate : null_token {
102+
assoc try_associate() noexcept {
103+
return {};
104+
}
105+
};
106+
107+
struct non_const_wrap : null_token {
108+
template <ex::sender Sender>
109+
Sender&& wrap(Sender&& snd) noexcept {
110+
return std::forward<Sender>(snd);
111+
}
112+
};
113+
114+
TEST_CASE("Scope token concept rejects non-token types", "[concepts][scope_token]") {
115+
STATIC_REQUIRE(!ex::scope_token<int>);
116+
117+
// tokens must be copyable
118+
STATIC_REQUIRE(!ex::scope_token<move_only>);
119+
120+
// try_associate must be const-qualified
121+
STATIC_REQUIRE(!ex::scope_token<non_const_try_associate>);
122+
123+
// wrap must be const-qualified
124+
STATIC_REQUIRE(!ex::scope_token<non_const_wrap>);
125+
}
126+
} // namespace

0 commit comments

Comments
 (0)