mercredi 6 juillet 2016

using std::bind() on overloaded class method

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://ift.tt/29gQod0

#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;
}

Aucun commentaire:

Enregistrer un commentaire