Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

basic_stream's (websocket::stream) async_connect compilation (type not convertible) failure introduced between Boost 1.85.0 and Boost 1.86.0 #2936

Open
13steinj opened this issue Oct 5, 2024 · 3 comments

Comments

@13steinj
Copy link

13steinj commented Oct 5, 2024

Version of Beast

Boost 1.86.0

Steps necessary to reproduce the problem

Sample code and godbolt (yes I know this minimum example is contrived, but I don't want to share awful code know what's intellectual property and what isn't in this section of the codebase).

#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>

namespace bb = boost::beast;

extern boost::beast::websocket::stream<boost::beast::tcp_stream>* stream_;
extern void onConnect(boost::beast::error_code ec, boost::asio::ip::tcp::resolver::results_type::endpoint_type);

void onResolve(boost::beast::error_code ec, boost::asio::ip::tcp::resolver::results_type results) {
    boost::beast::get_lowest_layer(*stream_).async_connect(
        results, [](auto ec, auto endpoint) { onConnect(ec, endpoint); });
}

Simple "solution" is to change auto endpoint to boost::asio::ip::tcp::resolver::results_type::endpoint_type... but I have absolutely no idea if this changes function generation / execution compared to what's intended; it appears as though this is a mixed Asio/Beast bug. Reduced to a few commits in Boost Asio / Boost Beast, specifically:

Asio had no release notes available in the Boost release, I suspect this is a case of "Asio changed a default, Beast changed [to fix ambiguity in #2867 via #2893] but introduced a different behavior change by accident."

All relevant compiler information

I can't think of anything that is particularly relevant, but GCC 13.3 (or 14.2); -std=c++26 is enough to trigger this.

@ashtum
Copy link
Collaborator

ashtum commented Oct 5, 2024

This appears to be an issue in Asio, as the following code fails to compile:

boost::asio::async_connect(
    boost::beast::get_lowest_layer(*stream_).socket(),
    results,
    [](auto ec, auto endpoint) { onConnect(ec, endpoint); });

A workaround is to add a trailing return type to the lambda, which ensures that the is_connect_condition trait works correctly:

boost::asio::async_connect(
    boost::beast::get_lowest_layer(*stream_).socket(),
    results,
    [](auto ec, auto endpoint) -> void { onConnect(ec, endpoint); });

I recommend opening an issue in Asio so that this can be addressed.
it seems the issue is that the generic lambda passed to async_connect made it SFINAE-unfriendly. You can either use a trailing return type or a concrete type for the endpoint argument.

@13steinj
Copy link
Author

13steinj commented Oct 7, 2024

I recommend opening an issue in Asio so that this can be addressed.
it seems the issue is that the generic lambda passed to async_connect made it SFINAE-unfriendly. You can either use a trailing return type or a concrete type for the endpoint argument.

Sorry, I don't fully follow-- I mean, yes, but the example you provided that fails to compile fails in both 1.85 and 1.86. The example I provided only fails in 1.86, they appear to be related examples but the semantics are different? The notable thing is the lambda's template operator() gets instantiated with at least the endpoint type and with the ...resolver_iterator... type.

The odder thing is... I'm a bit surprised that explicitly specifying the return type is another valid workaround.

@ashtum
Copy link
Collaborator

ashtum commented Oct 7, 2024

The odder thing is... I'm a bit surprised that explicitly specifying the return type is another valid workaround.

Chris added the is_connect_condition type trait to resolve ambiguity in several overloads of async_connect. The generic lambda you passed to async_connect made it SFINAE-unfriendly, causing the evaluation of is_connect_condition to result in a compilation error. By adding a trailing return type or using a concrete type for the second parameter, the type trait can detect that the lambda is not a connect_condition earlier without the instantiation of the lambda's call operator.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants