Why can't std::bind and boost::bind be used interchangeably in this Boost.Asio tutorials
Asked Answered
E

4

19

I was trying the differents tutorials in Boost.Asio documentation and tried to replace boost components with C++11 ones. However, I got an error using std::bind in Timer.5 - Synchronising handlers in multithreaded programs. Here is the code proposed:

#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

class printer { /* Not relevent here */ };

int main()
{
  boost::asio::io_service io;
  printer p(io);
  boost::thread t(boost::bind(&boost::asio::io_service::run, &io));
  io.run();
  t.join();

  return 0;
}

I tried to replace boost::thread by std::thread and boost::bind by std::bind. Here is my code:

#include <functional>
#include <iostream>
#include <thread>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

class printer { /* Not relevent here */ };

int main() {
    boost::asio::io_service io;
    printer p(io);
    std::thread t(std::bind(&boost::asio::io_service::run, &io));
    io.run();
    t.join();
}

When compiling with GCC 4.7, I got this compile-time error:

g++ -std=c++0x main.cpp -lboost_system -lboost_date_time -lpthread
main.cpp: In function ‘int main()’:
main.cpp:52:60: erreur: no matching function for call to ‘bind(<unresolved overloaded function type>, boost::asio::io_service*)’
main.cpp:52:60: note: candidates are:
/usr/include/c++/4.6/functional:1444:5: note: template<class _Functor, class ... _ArgTypes> typename std::_Bind_helper::type std::bind(_Functor&&, _ArgTypes&& ...)
/usr/include/c++/4.6/functional:1471:5: note: template<class _Result, class _Functor, class ... _ArgTypes> typename std::_Bindres_helper::type std::bind(_Functor&&, _ArgTypes&& ...)

Where is this error comming from, taking into account that I did not use any boost::asio::placeholders (as explain in this stackoverflow question Should std::bind be compatible with boost::asio?)?

Essive answered 28/1, 2012 at 19:59 Comment(2)
Possible Duplicate: see #8924649).Cornucopia
Since you're already using C++11: lambdas may be an alternative to std::bind for you, e.g., std::thread t([&io]() { io.run(); });. This avoid the overload resolution entirely.Cristinecristiona
P
37

The boost::asio::io_service::run() member function is overloaded: one version takes no argument while another version takes one argument. That is, taking the address of the of boost::asio::io_service::run requires a context in which the compiler can directly deduce the signature of the function. However, std::bind() isn't required to do deduction magic while it seems that boost::bind() attempts to locate a matching overload i.e. it seems for its first argument type to be readily constrained (assuming the boost example indeed compiles).

The work-around this problem you can explicitly specify the type of the first argument to std::bind() (it should also work with boost::bind()) e.g. like this:

std::bind(static_cast<size_t (boost::asio::io_service::*)()>(&boost::asio::io_service::run), &io);

I haven't checked whether the standard makes any requirements but if it indeed doesn't do any requirements I would consider an implementation which does not go to heroics to deduce the argument type to be of better quality although it does less work: it requires that the user writes code which can compile unchanged on another compiler.

Polyamide answered 28/1, 2012 at 20:39 Comment(0)
C
19

Just a quick note, in C++11 onwards you can just use lambdas to avoid all the faff and simplify the whole thing greatly. The old:

boost::thread t(boost::bind(&boost::asio::io_service::run, &io));

or the std::version:

std::thread t(boost::bind(static_cast<size_t (boost::asio::io_service::*)()>(&boost::asio::io_service::run), &io_service));

become just:

std::thread t([&io_service](){io_service.run();});
Chivalric answered 12/11, 2014 at 19:59 Comment(2)
just so much better!Bronchiole
Often you would like to put that thread into a vector. but written as the lambda the vector is not movable so you would need to use threadPool.emplace_back(std::move(t))Franky
T
1

This was a huge PITA to figure out, so thank you Dietmar for the hint. for those using the boost::bind, you solution looks like this:

// create the io_service    

boost::asio::io_service io_service;

 // assign some work to asio before starting
 {
    io_service.post(&some_work);   // just for example
    ....
 }

boost::thread t(boost::bind(static_cast<size_t (boost::asio::io_service::*)()>(&boost::asio::io_service::run), &io_service));

// work should be executed in a new thread

t.join()

return;
Tartu answered 2/1, 2013 at 19:33 Comment(0)
P
0

Since you're already using C++11: lambdas may be an alternative to std::bind for you, e.g., std::thread t(&io { io.run(); });. This avoid the overload resolution entirely.

To get the right output, the solution(in asio standalone or boost::asio) is:

asio::io_service io;
auto ptrToIoService = &io;
printer p(io);

//asio::thread t(std::bind(&asio::io_service::run, &io));
//asio::thread t([&io]() {io.run();});
asio::thread t([ptrToIoService] () { ptrToIoService->run();});

See "Effective Modern C++" under "Item 31 Avoid default capture modes."

Pallmall answered 26/6, 2016 at 2:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.