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 <boost/cobalt.hpp>
#include <boost/cobalt/io.hpp>
#include <iostream>
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;
}
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: