boostorg / boostorg/cobalt

Race() won't compile if as_tuple() or as_result() used to wrap awaitable parameter(s)

Open
#271 0 comments 0 reactions 0 assignees View on GitHub
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.