Race() won't compile if as_tuple() or as_result() used to wrap awaitable parameter(s)
- Dominant language
- C++
- Stars
- 347
- Forks
- 47
- Avg merge
- 3d 8h
- Merged PRs (30d)
- 1
Description
Tested against develop and 1.90.0 tag, on Linux using gcc 14.2.0, -std=c++23.
It doesn't appear to be possible to use as_tuple() or as_result() to wrap awaiter parameters to race().
I want to use this approach as it makes it easier to determine the source of an error, rather than relying on catching system_error from race().
I am unsure if this is by design, and couldn't immediately see a workaround other than not using these conversion wrappers.
Reproducer:
```
#include
#include
#include
using namespace boost;
using namespace std::chrono_literals;
// Uncomment one or none
// #define USE_AS_TUPLE
// #define USE_AS_RESULT
cobalt::main co_main(int, char *[]) {
std::cout << "Starting co_main\nEnter a character: " << std::flush;
cobalt::io::stream_file stdin_stream(::dup(STDIN_FILENO));
cobalt::io::steady_timer timer(10s);
char buffer[1];
// Note: stream_file::read_some() used here rather than cobalt::read() because
// read() doesn't cancel when race is won by timer (see Cobalt Github Issue 270)
#if defined(USE_AS_TUPLE)
// Won't compile:
auto result =
co_await cobalt::race(as_tuple(stdin_stream.read_some(asio::buffer(buffer, 1))), as_tuple(timer.wait()));
switch (result.index()) {
case 0: {
auto [ec, n] = get<0>(result);
if (ec) {
std::cout << "Read error: " << ec.what() << std::endl;
} else {
std::cout << "Read: " << n << " byte: " << std::string_view(buffer, n) << std::endl;
}
break;
}
case 1: {
auto [ec] = get<1>(result);
if (ec) {
std::cout << "Timer error: " << ec.what() << std::endl;
} else {
std::cout << "Read timed out\n";
}
break;
}
}
#elif defined(USE_AS_RESULT)
// Wont compile:
auto result =
co_await cobalt::race(as_result(stdin_stream.read_some(asio::buffer(buffer, 1))), as_result(timer.wait()));
switch (result.index()) {
case 0: {
auto r = get<0>(result);
if (r.error()) {
std::cout << "Read error: " << r.error().what() << std::endl;
} else {
std::cout << "Read: " << *r << " byte: " << std::string_view(buffer, *r) << std::endl;
}
break;
}
case 1: {
auto r = get<1>(result);
if (r.error()) {
std::cout << "Timer error: " << r.error().what() << std::endl;
} else {
std::cout << "Read timed out\n";
}
break;
}
}
#else
// Will compile
try {
auto result = co_await cobalt::race(stdin_stream.read_some(asio::buffer(buffer, 1)), timer.wait());
switch (result.index()) {
case 0: {
auto r = get<0>(result);
std::cout << "Read: " << r << " byte: " << std::string_view(buffer, r) << std::endl;
break;
}
case 1: {
std::cout << "Read timed out\n";
break;
}
}
} catch (system::system_error & ex) {
std::cout << "Exception: " << ex.what() << std::endl;
}
#endif
std::cout << "Done\n";
co_return 0;
}
```
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by compiling the provided reproducer against develop with each of USE_AS_TUPLE and USE_AS_RESULT enabled, then inspect the race(), as_tuple(), and as_result() entry points involved in those calls. The issue is done when both wrapped awaiter forms compile and retain the reported result handling, with the unwrapped race() behavior still working.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100