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

fix: bind_* associating functions must respect value-category of wrapped object #1540

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

justend29
Copy link
Contributor

Issue

The helper functions for binding associations to completion handlers, such as bind_executor, did not previously respect the value-category of wrapper. Consequently, when using move-only values with certain completion-handlers, build failures result since although the wrapper was an r-value, the wrapped target was not treated as such.

Changes

The solution is to forward the value-category of the wrapper to the wrapped target. This is already done in other wrappers in asio, like append. For these bind wrappers, an overload of their forwarding call operator is added to each wrapper. Moving the target is in C++03 form, using static_cast over std::move, to remain consistent with the rest of asio.

Failing Example

Although contrived, this example fails to build because the move-only completion handler Intermediary is not handled properly by bind_allocator. I've hit this build failure in multiple more realistic scenarios, particularly when using asio::awaitable, where the result of the awaitable frame cannot be allocated here due to the improper value-category.

#include <iostream>

#include <boost/asio.hpp>
#include <boost/system.hpp>

namespace asio = boost::asio;
namespace sys = boost::system;

using Signature = void(sys::error_code);

struct Intermediary {
  Intermediary(asio::any_completion_handler<Signature> completion)
      : _completion{std::move(completion)}
  {}

  Intermediary(const Intermediary&) = delete;
  Intermediary(Intermediary&&) = default;

  asio::any_completion_handler<Signature> _completion;

  void operator()() && { std::move(_completion)(sys::error_code{}); }
};

template <asio::completion_token_for<Signature> Token>
auto get_move_only(Token&& token)
{
  auto initiate = []<typename C>(C&& completion) {
    // Fails:
    asio::dispatch(
        asio::bind_allocator(std::allocator<void>{}, Intermediary{std::forward<C>(completion)}));

    // Success:
    // asio::dispatch(Intermediary{std::forward<C>(completion)});
  };

  return asio::async_initiate<Token, Signature>(initiate, token);
}

int main()
{
  asio::io_context ioc{};

  get_move_only([](const sys::error_code err){
    std::cout << err.message() << std::endl;
  });

  ioc.run();
}

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

Successfully merging this pull request may close these issues.

1 participant