Reference: boost_1_78_0/doc/html/boost_asio/reference/ip__basic_resolver/async_resolve/overload1.html

template<
    typename ResolveHandler = DEFAULT>
DEDUCED async_resolve(
    const query & q,
    ResolveHandler && handler = DEFAULT);

The handler to be called when the resolve operation completes. Copies will be made of the handler as required. The function signature of the handler must be:

void handler(
  const boost::system::error_code& error, // Result of operation.
  resolver::results_type results // Resolved endpoints as a range.
);

boost_1_78_0/libs/beast/example/websocket/client/async-ssl/websocket_client_async_ssl.cpp

void run(char const *host, char const *port, char const *text) {
...
  resolver_.async_resolve(
      host, port,
      beast::bind_front_handler(&session::on_resolve, shared_from_this()));
}

void on_resolve(beast::error_code ec, tcp::resolver::results_type results) {
  if (ec)
    return fail(ec, "resolve");

  // Set a timeout on the operation
  beast::get_lowest_layer(ws_).expires_after(std::chrono::seconds(30));

  // Make the connection on the IP address we get from a lookup
  beast::get_lowest_layer(ws_).async_connect(
      results,
      beast::bind_front_handler(&session::on_connect, shared_from_this()));
}

Question 1> Why does the on_resolve use the following function signature?

on_resolve(beast::error_code ec, tcp::resolver::results_type results)

As shown above, the first parameter(i.e. ec) is taken as pass-by value. This happens almost in all other functions which take a beast::error_code as an input parameter within sample code.

Instead of

on_resolve(const beast::error_code& ec, tcp::resolver::results_type results)

Question 2> Why doesn't the documentation suggest using the following instead?

on_resolve(const beast::error_code& ec, const tcp::resolver::results_type& results)

Thank you

1

There are 1 answers

4
sehe On BEST ANSWER

It's a cultural difference between Asio and Beast if you will.

UPDATE

There's some contention about my initial response.

It turns out that at least Boost System's error_code recently got endowed with shiny new (non-standard) features, that makes it bigger. Perhaps big enough to make it more efficient to pass by reference.

In the words of Vinnie Falco: This needs to be studied again.

Rationale

In Asio, the standard "doctrine" is to take error_code by const&. In Beast, the standard practice is actually to pass by value, which is, IMO, how error_code is intended.

In essence, error_code is just a tuple of (int, error_category const*) which is trivially copied and therefore optimized. Passing by value allows compilers much more room for optimization, especially when inlining. A key factor is that value-arguments never create aliasing opportunities.

(I can try to find a reference as I think some Beast devs are on record explaining this rationale.)

Why is it OK?

Any function that takes T by value is delegation-compatible with the requirement that it takes T by const reference, as long as T is copyable.

Other thoughts

There may have been historical reasons why Asio preferred, or even mandated error_code const& in the past, but as far as I am aware, any of these reasons are obsolete.