Skip to content

Commit 7c24c2b

Browse files
authored
🐛 (patch) Add missing default ctor for future<void> (#62)
This allows sync functions to return a finished future<void>
1 parent 0457f50 commit 7c24c2b

2 files changed

Lines changed: 112 additions & 47 deletions

File tree

modules/async_context.cppm

Lines changed: 65 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,71 @@ public:
893893
using handle_type = std::coroutine_handle<>;
894894
using full_handle_type = std::coroutine_handle<promise_type>;
895895

896+
future(future const& p_other) = delete;
897+
future& operator=(future const& p_other) = delete;
898+
899+
/**
900+
* @brief Default initialization for a void future
901+
*
902+
* This future will considered to be done on creation.
903+
*/
904+
future()
905+
requires(std::is_void_v<T>)
906+
: m_state(std::monostate{})
907+
{
908+
}
909+
910+
/**
911+
* @brief Construct a future with a value
912+
*
913+
* This future will considered to be done and will contain just the value
914+
* passed into this.
915+
*
916+
* @tparam U - type that can construct a type T (which includes T itself)
917+
*/
918+
template<typename U>
919+
constexpr future(U&& p_value) noexcept
920+
requires std::is_constructible_v<T, U&&>
921+
{
922+
m_state.template emplace<T>(std::forward<U>(p_value));
923+
};
924+
925+
constexpr future(future&& p_other) noexcept
926+
: m_state(std::exchange(p_other.m_state, std::monostate{}))
927+
{
928+
if (std::holds_alternative<handle_type>(m_state)) {
929+
auto handle = std::get<handle_type>(m_state);
930+
full_handle_type::from_address(handle.address())
931+
.promise()
932+
.set_object_address(&m_state);
933+
}
934+
}
935+
936+
constexpr future& operator=(future&& p_other) noexcept
937+
{
938+
if (this != &p_other) {
939+
m_state = std::exchange(p_other.m_state, std::monostate{});
940+
if (std::holds_alternative<handle_type>(m_state)) {
941+
auto handle = std::get<handle_type>(m_state);
942+
full_handle_type::from_address(handle.address())
943+
.promise()
944+
.set_object_address(&m_state);
945+
}
946+
}
947+
return *this;
948+
}
949+
950+
constexpr ~future()
951+
{
952+
if (std::holds_alternative<handle_type>(m_state)) {
953+
auto handle = std::get<handle_type>(m_state);
954+
full_handle_type::from_address(handle.address())
955+
.promise()
956+
.get_context()
957+
.cancel();
958+
}
959+
}
960+
896961
constexpr void resume() const
897962
{
898963
if (std::holds_alternative<handle_type>(m_state)) {
@@ -1023,52 +1088,6 @@ public:
10231088
return awaiter{ *this };
10241089
}
10251090

1026-
template<typename U>
1027-
constexpr future(U&& p_value) noexcept
1028-
requires std::is_constructible_v<T, U&&>
1029-
{
1030-
m_state.template emplace<T>(std::forward<U>(p_value));
1031-
};
1032-
1033-
future(future const& p_other) = delete;
1034-
future& operator=(future const& p_other) = delete;
1035-
1036-
constexpr future(future&& p_other) noexcept
1037-
: m_state(std::exchange(p_other.m_state, std::monostate{}))
1038-
{
1039-
if (std::holds_alternative<handle_type>(m_state)) {
1040-
auto handle = std::get<handle_type>(m_state);
1041-
full_handle_type::from_address(handle.address())
1042-
.promise()
1043-
.set_object_address(&m_state);
1044-
}
1045-
}
1046-
1047-
constexpr future& operator=(future&& p_other) noexcept
1048-
{
1049-
if (this != &p_other) {
1050-
m_state = std::exchange(p_other.m_state, std::monostate{});
1051-
if (std::holds_alternative<handle_type>(m_state)) {
1052-
auto handle = std::get<handle_type>(m_state);
1053-
full_handle_type::from_address(handle.address())
1054-
.promise()
1055-
.set_object_address(&m_state);
1056-
}
1057-
}
1058-
return *this;
1059-
}
1060-
1061-
constexpr ~future()
1062-
{
1063-
if (std::holds_alternative<handle_type>(m_state)) {
1064-
auto handle = std::get<handle_type>(m_state);
1065-
full_handle_type::from_address(handle.address())
1066-
.promise()
1067-
.get_context()
1068-
.cancel();
1069-
}
1070-
}
1071-
10721091
private:
10731092
friend promise_type;
10741093

tests/basics.test.cpp

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,28 @@ import test_utils;
88
boost::ut::suite<"basics"> basics = []() {
99
using namespace boost::ut;
1010

11-
"sync return"_test = []() {
11+
"sync return type void"_test = []() {
12+
// Setup
13+
test_context ctx;
14+
15+
unsigned step = 0;
16+
auto sync_coroutine = [&step](async::context&) -> async::future<void> {
17+
step = 1;
18+
return {};
19+
};
20+
21+
// Exercise
22+
auto future = sync_coroutine(ctx);
23+
24+
// Verify
25+
expect(that % 0 == ctx.memory_used());
26+
expect(that % not ctx.info->scheduled_called_once);
27+
expect(that % future.done());
28+
expect(that % future.has_value());
29+
expect(that % 1 == step);
30+
};
31+
32+
"sync return type int"_test = []() {
1233
// Setup
1334
test_context ctx;
1435

@@ -32,6 +53,31 @@ boost::ut::suite<"basics"> basics = []() {
3253
expect(that % 1 == step);
3354
};
3455

56+
"sync return type std::string"_test = []() {
57+
// Setup
58+
test_context ctx;
59+
60+
static constexpr auto expected_return_value = "Hello, World\n";
61+
62+
unsigned step = 0;
63+
auto sync_coroutine =
64+
[&step](async::context&) -> async::future<std::string> {
65+
step = 1;
66+
return expected_return_value;
67+
};
68+
69+
// Exercise
70+
auto future = sync_coroutine(ctx);
71+
72+
// Verify
73+
expect(that % 0 == ctx.memory_used());
74+
expect(that % not ctx.info->scheduled_called_once);
75+
expect(that % future.done());
76+
expect(that % future.has_value());
77+
expect(that % expected_return_value == future.value());
78+
expect(that % 1 == step);
79+
};
80+
3581
"co_return"_test = []() {
3682
// Setup
3783
test_context ctx;

0 commit comments

Comments
 (0)