7

I'm on CentOS 6.6 (gcc 4.4.7) and developing with Boost.Asio (1.41). I'd like io_service to call member function run() in manger object m when it starts. The code I'm trying to compile looks like:

#include <memory>
#include <boost/asio.hpp>
#include <boost/bind.hpp>

boost::asio::io_service io;
std::unique_ptr<manager> m;
m = std::make_unique<manager>;
io.post(boost::bind(&manager::run, &m));

gcc pitches a fit on the boost::bind statement, which includes:

/usr/include/boost/bind/mem_fn_template.hpp:40: error: pointer to
member type ‘void (manager::)()’ incompatible with object type
‘std::unique_ptr<manager, std::default_delete<manager> >’

What do I want to be doing here?

The manager object will only know about timers; a separate object that knows about io_service will get added to its constructor later. But the idea is that manager::run() will create an initial set of timers to bootstrap the system.

Clarification:

My thinking here is that the outer block of code manages the lifetime of m and that the next statement will be io.run(). The outer code will destroy m when io.run() returns. Hence, passing a raw reference for m to io is appropriate. But I'm a modern C++ novice and could be way off base here.

  • You don't pass a reference of any kind to `bind` -- you pass a regular pointer to a unique pointer. But unique pointers must be unique, and with your code, when the `post` returns, there are two -- one in the posted `bind` and one in `m`. That obviously can't be right. Perhaps you want `boost::bind (&manager::run, std::move(m))`? – David Schwartz Mar 02 '15 at 22:50
  • `boost::bind` doesn't know how to unwrap a `unique_ptr`, you need pass it either a `manager *` (`m.get()`) or a `manager` instance (`*m`) (the second one makes a copy of the object `m` points to). And I find it difficult to believe you're using gcc4.4.7; that compiler doesn't even understand `-std=c++11`, let alone `std::make_unique`, a C++14 addition. – Praetorian Mar 02 '15 at 23:47
  • gcc 4.4.7 has `-std=gnu++0x`, and for `make_unique` I snagged [STL's suggestion](http://stackoverflow.com/questions/17902405/how-to-implement-make-unique-function-in-c11). – Andreas Yankopolus Mar 03 '15 at 02:25

1 Answers1

5

You'd need C++-14 and generalized lambda capture to make this work -- you'd need to move the unique pointer into the lambda. Instead, just use a shared_ptr, which std::bind understands natively:

std::shared_ptr<manager> m;
m = std::make_shared<manager>();
io.post(std::bind(&manager::run, std::move(m)));

The std::move is optional but ensures that m doesn't keep the manager around when it's not wanted.

Community
  • 1
  • 1
David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • I feel dirty doing this, at least with my current understanding of the implications of unique vs. shared pointers. `io` won't be responsible for the lifecycle of `m`. The way the program is headed, `m` will get cleaned up after `io.run()` (which will come shortly after `io.post()`) returns. Does `make_shared()` give the reader the wrong idea? – Andreas Yankopolus Mar 03 '15 at 16:04
  • 1
    @AndreasYankopolus The `m` variable will get cleaned up, but the `bind` will capture the shared pointer by value, keeping the manager around as long as the `bind` stays around, which it must until its execution completes. – David Schwartz Mar 03 '15 at 19:55
  • 1
    Is it Bad and Wrong to keep `m` as a `unique_ptr` and change the bind statement to: `boost::bind(&manager::run, m.get())`? – Andreas Yankopolus Mar 03 '15 at 20:00
  • 1
    @AndreasYankopolus Yes, because then the manager can be deleted too soon (as soon as `m` goes out of scope). You need the `bind` to keep the manager alive and that requires it to capture something, by value, that keeps the manager around. – David Schwartz Mar 03 '15 at 20:56