3) Asio has the concept of error dispositions [6]. In a nutshell, these are a generalization of error codes, so custom error types can be used in error code-aware functionality, like when_any. This is useful in code using specialized types. Future candidate Boost.Http uses this in its router [8], and I intended to use it as a way to attach error messages in my database libraries. I discuss my use case in more detail in this issue [9]. Have you considered the feature?
[6] https://www.boost.org/doc/libs/latest/doc/html/boost_asio/reference/Disposit...
Would you be able to provide an example illustrating how you would use this?
My use case is including a diagnostic string returned by the server in the error type. For instance: struct extended_error { std::error_code code; std::string diag; }; // complete type in my postgres library here [1] Then you can define your Asio function like this: class connection { public: template <asio::completion_token_for<void(extended_error)> CompletionToken> auto async_connect(const connect_params& params, CompletionToken&& token); }; // complete type here [2] With Capy, I currently have: class connection { public: // *diag will be populated in case of server error boost::capy::io_task<> connect(connect_params params, std::string* diag = nullptr); }; Before anyone asks, I can go down the path of "only network errors are failures" and write: // Succeeds if the server rejects the connection - populates server_errors in this case boost::capy::io_task<> connect(connect_params params, extended_error& server_errors); This is adequate for low-level functionality, but becomes hostile as you build higher level functionality. For now, I've encoded the SQLSTATE returned by the server as a std::error_code, so the user gets _some_ diagnostics even if they didn't use the output parameter [3]. Because extended_error is no longer trivially copyable, I don't know the performance implications that such dispositions may have. I am open to discussion of what's the best way of doing this with Capy. It doesn't have to be dispositions. Another case for dispositions would be supporting custom error types, like boost::system::error_code. ATM this doesn't compile: capy::task<boost::system::error_code> my_task() { co_return {}; } capy::task<void> co_main() { co_await capy::timeout(my_task(), 1s); } I personally don't need this. Thanks, Ruben. [1] https://github.com/anarthal/nativepg/blob/master/include/nativepg/extended_e... [2] https://github.com/anarthal/nativepg/blob/master/include/nativepg/connection... [3] https://github.com/anarthal/nativepg/blob/master/include/nativepg/sqlstate.h...