0

i'm sure this must be an common issue so apologies if it's just my search skills that are failing.

i'm trying to do this without boost because the functionality exists in C++11 and i'm a fan of standards.

i'm trying to use std::bind() to wrap a method in a class from an external library. (specifically: asio::io_service::run() ) unfortunately the method is overloaded which confuses std::bind().

minimal test case to demonstrate the issue:

#include <functional>   // std::bind

class Test{
 public:
  void method(){}
  void method(int i){}
};

int main(int argc, char * argv[]) {
  Test test_instance;
  auto bound_thing_1 = std::bind(Test::method, test_instance);
  return 0;
}

and compile:

duncan@casper:/tmp/bind_test$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.3.1-14ubuntu2.1' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 5.3.1 20160413 (Ubuntu 5.3.1-14ubuntu2.1) 
duncan@casper:/tmp/bind_test$ /usr/bin/g++ -Wall -std=c++11 ./main.c -o test.bin
./main.c: In function ‘int main(int, char**)’:
./main.c:12:61: error: no matching function for call to ‘bind(<unresolved overloaded function type>, Test&)’
   auto bound_thing_1 = std::bind(Test::method, test_instance);
                                                             ^
In file included from /usr/include/c++/5/memory:79:0,
                 from /usr/include/asio/detail/addressof.hpp:21,
                 from /usr/include/asio/detail/handler_alloc_helpers.hpp:19,
                 from /usr/include/asio/detail/bind_handler.hpp:19,
                 from /usr/include/asio/detail/wrapped_handler.hpp:18,
                 from /usr/include/asio/io_service.hpp:24,
                 from /usr/include/asio/basic_io_object.hpp:19,
                 from /usr/include/asio/basic_socket.hpp:20,
                 from /usr/include/asio/basic_datagram_socket.hpp:20,
                 from /usr/include/asio.hpp:19,
                 from ./main.c:1:
/usr/include/c++/5/functional:1462:5: note: candidate: template<class _Func, class ... _BoundArgs> typename std::_Bind_helper<std::__is_socketlike<_Func>::value, _Func, _BoundArgs ...>::type std::bind(_Func&&, _BoundArgs&& ...)
     bind(_Func&& __f, _BoundArgs&&... __args)
     ^
/usr/include/c++/5/functional:1462:5: note:   template argument deduction/substitution failed:
./main.c:12:61: note:   couldn't deduce template parameter ‘_Func’
   auto bound_thing_1 = std::bind(Test::method, test_instance);
                                                             ^
In file included from /usr/include/c++/5/memory:79:0,
                 from /usr/include/asio/detail/addressof.hpp:21,
                 from /usr/include/asio/detail/handler_alloc_helpers.hpp:19,
                 from /usr/include/asio/detail/bind_handler.hpp:19,
                 from /usr/include/asio/detail/wrapped_handler.hpp:18,
                 from /usr/include/asio/io_service.hpp:24,
                 from /usr/include/asio/basic_io_object.hpp:19,
                 from /usr/include/asio/basic_socket.hpp:20,
                 from /usr/include/asio/basic_datagram_socket.hpp:20,
                 from /usr/include/asio.hpp:19,
                 from ./main.c:1:
/usr/include/c++/5/functional:1490:5: note: candidate: template<class _Result, class _Func, class ... _BoundArgs> typename std::_Bindres_helper<_Result, _Func, _BoundArgs>::type std::bind(_Func&&, _BoundArgs&& ...)
     bind(_Func&& __f, _BoundArgs&&... __args)
     ^
/usr/include/c++/5/functional:1490:5: note:   template argument deduction/substitution failed:
./main.c:12:61: note:   couldn't deduce template parameter ‘_Result’
   auto bound_thing_1 = std::bind(Test::method, test_instance);

so the problem there (as i interpret it) is that std::bind does not know whether to use method() or method(int).

my next step was to explicitly tell std::bind() which method:

#include <functional>   // std::bind

class Test{
 public:
  void method(){}
  void method(int i){}
};

int main(int argc, char * argv[]) {
  Test test_instance;
  // auto bound_thing_1 = std::bind(Test::method, test_instance);
  auto bound_thing_2 = std::bind(static_cast<void(Test::*)(void)>(&Test::method), test_instance);
  return 0;
}

this builds correctly. (yay!)

so now i try to apply the same logic using my desired library: http://think-async.com/Asio/asio-1.10.6/doc/asio/reference/io_service/run.html

#include <asio.hpp>
#include <thread>         // std::thread
#include <functional>   // std::bind

int main(int argc, char * argv[]) {
  asio::io_service io_service_instance;
  auto bound_thing_3 = std::bind(static_cast<std::size_t(asio::io_service::*)(void)>(&asio::io_service::run), io_service_instance);

  std::thread mythread(bound_thing_3)
  return 0;
}

and the compile fails:

duncan@casper:/tmp/bind_test$ /usr/bin/g++ -Wall -std=c++11 ./main.c -o test.bin -lpthread
In file included from /usr/include/c++/5/functional:55:0,
                 from /usr/include/c++/5/memory:79,
                 from /usr/include/asio/detail/addressof.hpp:21,
                 from /usr/include/asio/detail/handler_alloc_helpers.hpp:19,
                 from /usr/include/asio/detail/bind_handler.hpp:19,
                 from /usr/include/asio/detail/wrapped_handler.hpp:18,
                 from /usr/include/asio/io_service.hpp:24,
                 from /usr/include/asio/basic_io_object.hpp:19,
                 from /usr/include/asio/basic_socket.hpp:20,
                 from /usr/include/asio/basic_datagram_socket.hpp:20,
                 from /usr/include/asio.hpp:19,
                 from ./main.c:1:
/usr/include/c++/5/tuple: In instantiation of ‘constexpr std::_Head_base<_Idx, _Head, false>::_Head_base(_UHead&&) [with _UHead = asio::io_service; long unsigned int _Idx = 0ul; _Head = asio::io_service]’:
/usr/include/c++/5/tuple:369:49:   required from ‘constexpr std::_Tuple_impl<_Idx, _Head>::_Tuple_impl(std::_Tuple_impl<_Idx, _Head>&&) [with long unsigned int _Idx = 0ul; _Head = asio::io_service]’
/usr/include/c++/5/tuple:484:17:   required from ‘std::_Bind<_Functor(_Bound_args ...)>::_Bind(std::_Bind<_Functor(_Bound_args ...)>&&) [with _Functor = std::_Mem_fn<long unsigned int (asio::io_service::*)()>; _Bound_args = {asio::io_service}]’
./main.c:16:130:   required from here
/usr/include/c++/5/tuple:115:42: error: use of deleted function ‘asio::io_service::io_service(const asio::io_service&)’
  : _M_head_impl(std::forward<_UHead>(__h)) { }
                                          ^
In file included from /usr/include/asio/basic_io_object.hpp:19:0,
                 from /usr/include/asio/basic_socket.hpp:20,
                 from /usr/include/asio/basic_datagram_socket.hpp:20,
                 from /usr/include/asio.hpp:19,
                 from ./main.c:1:
/usr/include/asio/io_service.hpp:184:7: note: ‘asio::io_service::io_service(const asio::io_service&)’ is implicitly deleted because the default definition would be ill-formed:
 class io_service
       ^
In file included from /usr/include/asio/io_service.hpp:23:0,
                 from /usr/include/asio/basic_io_object.hpp:19,
                 from /usr/include/asio/basic_socket.hpp:20,
                 from /usr/include/asio/basic_datagram_socket.hpp:20,
                 from /usr/include/asio.hpp:19,
                 from ./main.c:1:
/usr/include/asio/detail/noncopyable.hpp:31:3: error: ‘asio::detail::noncopyable::noncopyable(const asio::detail::noncopyable&)’ is private
   noncopyable(const noncopyable&);
   ^
In file included from /usr/include/asio/basic_io_object.hpp:19:0,
                 from /usr/include/asio/basic_socket.hpp:20,
                 from /usr/include/asio/basic_datagram_socket.hpp:20,
                 from /usr/include/asio.hpp:19,
                 from ./main.c:1:
/usr/include/asio/io_service.hpp:184:7: error: within this context
 class io_service
       ^
In file included from /usr/include/c++/5/memory:79:0,
                 from /usr/include/asio/detail/addressof.hpp:21,
                 from /usr/include/asio/detail/handler_alloc_helpers.hpp:19,
                 from /usr/include/asio/detail/bind_handler.hpp:19,
                 from /usr/include/asio/detail/wrapped_handler.hpp:18,
                 from /usr/include/asio/io_service.hpp:24,
                 from /usr/include/asio/basic_io_object.hpp:19,
                 from /usr/include/asio/basic_socket.hpp:20,
                 from /usr/include/asio/basic_datagram_socket.hpp:20,
                 from /usr/include/asio.hpp:19,
                 from ./main.c:1:
/usr/include/c++/5/functional: In instantiation of ‘std::_Bind<_Functor(_Bound_args ...)>::_Bind(std::_Bind<_Functor(_Bound_args ...)>&&) [with _Functor = std::_Mem_fn<long unsigned int (asio::io_service::*)()>; _Bound_args = {asio::io_service}]’:
./main.c:16:130:   required from here
/usr/include/c++/5/functional:1120:78: note: synthesized method ‘constexpr std::tuple< <template-parameter-1-1> >::tuple(std::tuple< <template-parameter-1-1> >&&) [with _Elements = {asio::io_service}]’ first required here 
       : _M_f(std::move(__b._M_f)), _M_bound_args(std::move(__b._M_bound_args))
                                                                              ^
In file included from /usr/include/c++/5/functional:55:0,
                 from /usr/include/c++/5/memory:79,
                 from /usr/include/asio/detail/addressof.hpp:21,
                 from /usr/include/asio/detail/handler_alloc_helpers.hpp:19,
                 from /usr/include/asio/detail/bind_handler.hpp:19,
                 from /usr/include/asio/detail/wrapped_handler.hpp:18,
                 from /usr/include/asio/io_service.hpp:24,
                 from /usr/include/asio/basic_io_object.hpp:19,
                 from /usr/include/asio/basic_socket.hpp:20,
                 from /usr/include/asio/basic_datagram_socket.hpp:20,
                 from /usr/include/asio.hpp:19,
                 from ./main.c:1:
/usr/include/c++/5/tuple: In instantiation of ‘constexpr std::_Head_base<_Idx, _Head, false>::_Head_base(const _Head&) [with long unsigned int _Idx = 0ul; _Head = asio::io_service]’:
/usr/include/c++/5/tuple:357:21:   required from ‘constexpr std::_Tuple_impl<_Idx, _Head>::_Tuple_impl(const _Head&) [with long unsigned int _Idx = 0ul; _Head = asio::io_service]’
/usr/include/c++/5/tuple:473:33:   required from ‘constexpr std::tuple< <template-parameter-1-1> >::tuple(const _Elements& ...) [with _Elements = {asio::io_service}]’
/usr/include/c++/5/functional:1114:70:   required from ‘std::_Bind<_Functor(_Bound_args ...)>::_Bind(_Functor&&, _Args&& ...) [with _Args = {asio::io_service&}; _Functor = std::_Mem_fn<long unsigned int (asio::io_service::*)()>; _Bound_args = {asio::io_service}]’
/usr/include/c++/5/functional:1468:42:   required from ‘typename std::_Bind_helper<std::__is_socketlike<_Func>::value, _Func, _BoundArgs ...>::type std::bind(_Func&&, _BoundArgs&& ...) [with _Func = long unsigned int (asio::io_service::*)(); _BoundArgs = {asio::io_service&}; typename std::_Bind_helper<std::__is_socketlike<_Func>::value, _Func, _BoundArgs ...>::type = std::_Bind<std::_Mem_fn<long unsigned int (asio::io_service::*)()>(asio::io_service)>]’
./main.c:16:130:   required from here
/usr/include/c++/5/tuple:108:25: error: use of deleted function ‘asio::io_service::io_service(const asio::io_service&)’
       : _M_head_impl(__h) { }

the question: can anyone help me (or point me towards a source of information) understand what's going on here. i suspect the problem is related to the "static" nature of the static_cast...

this possible solution using a lambda function works but i'd be interested to hear opinions on whether it's the "correct" way to do things. personally i also find this easier to parse:

#include <asio.hpp>
#include <thread>         // std::thread
#include <functional>   // std::bind

int main(int argc, char * argv[]) {
  asio::io_service io_service_instance;

  auto bound_thing_4 = [&](){
    return io_service_instance.run();
  };
  std::thread mythread(bound_thing_4);
  return 0;
}

[EDIT] solution:

it appears instances of asio::io_service do not like to be copied:

#include <asio.hpp>

int main(int argc, char * argv[]) {
  asio::io_service io_service_instance;
  auto io_service_instance_2 = io_service_instance;

  return 0;
}

gives the compiler error:

duncan@casper:/tmp/bind_test$ /usr/bin/g++ -Wall -std=c++11 ./main.c -o test.bin -lpthread
./main.c: In function ‘int main(int, char**)’:
./main.c:5:32: error: use of deleted function ‘asio::io_service::io_service(const asio::io_service&)’
   auto io_service_instance_2 = io_service_instance;
                                ^
In file included from /usr/include/asio/basic_io_object.hpp:19:0,
                 from /usr/include/asio/basic_socket.hpp:20,
                 from /usr/include/asio/basic_datagram_socket.hpp:20,
                 from /usr/include/asio.hpp:19,
                 from ./main.c:1:
/usr/include/asio/io_service.hpp:184:7: note: ‘asio::io_service::io_service(const asio::io_service&)’ is implicitly deleted because the default definition would be ill-formed:
 class io_service
       ^
In file included from /usr/include/asio/io_service.hpp:23:0,
                 from /usr/include/asio/basic_io_object.hpp:19,
                 from /usr/include/asio/basic_socket.hpp:20,
                 from /usr/include/asio/basic_datagram_socket.hpp:20,
                 from /usr/include/asio.hpp:19,
                 from ./main.c:1:
/usr/include/asio/detail/noncopyable.hpp:31:3: error: ‘asio::detail::noncopyable::noncopyable(const asio::detail::noncopyable&)’ is private
   noncopyable(const noncopyable&);
   ^
In file included from /usr/include/asio/basic_io_object.hpp:19:0,
                 from /usr/include/asio/basic_socket.hpp:20,
                 from /usr/include/asio/basic_datagram_socket.hpp:20,
                 from /usr/include/asio.hpp:19,
                 from ./main.c:1:
/usr/include/asio/io_service.hpp:184:7: error: within this context
 class io_service

so the working solution involves passing by reference instead of copying (as per ecatmur's answer):

#include <asio.hpp>
#include <thread>         // std::thread
#include <functional>   // std::bind

int main(int argc, char * argv[]) {
  asio::io_service io_service_instance;
  auto bound_thing_3 = std::bind(
      static_cast<std::size_t(asio::io_service::*)(void)>(&asio::io_service::run),
      std::ref(io_service_instance));

  std::thread mythread(bound_thing_3);
  return 0;
}
mrdunk
  • 102
  • 1
  • 4

1 Answers1

2

If you want std::bind to capture its argument by reference, you need to use std::reference_wrapper:

auto bound_thing_3 = std::bind(static_cast<std::size_t(asio::io_service::*)(void)>(&asio::io_service::run),
    std::ref(io_service_instance));
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • ah. interesting. i believe that with std::bind() passing by reference is optional... so the problem here appears to be that instances of asio::io_service do not like to be copied. i'll add proof of that to my question. – mrdunk Jul 06 '16 at 11:09