I want to implement a manager that stores callbacks to member functions of polymorphic classes using C++11. The issue is that I am not sure how to handle the case where the object that the member belongs to gets deleted or should be deleted and I want to make the interface as simple as possible.
So I thought of the following: Store a std::weak_ptr
to the object as well as a std::function
to the member.
The following seems to work:
class MyBase {
public:
MyBase() {}
virtual ~MyBase() {}
};
//--------------------------------------------------
class MyClass : public MyBase {
public:
MyClass() : MyBase() {}
void myDouble(double val) const { std::cout << "Value is: " << val << std::endl; }
};
//--------------------------------------------------
Class Manager {
public:
void setFunction(std::weak_ptr<MyBase> base, std::function<void(double)> func) {
m_function.first = base;
m_function.second = func;
}
private:
std::pair<std::weak_ptr<MyBase>, std::function<void(double)>> m_function;
};
To use this:
Manager db;
std::shared_ptr<MyClass> myClass = std::make_shared<MyClass>();
db.setFunction(myClass, std::bind(&MyClass::myDouble, myClass, std::placeholders::_1));
Now I want to hide the std::bind
part from the user, so that he only needs to call:
db.setFunction(myClass, &MyClass::myDouble);
So I want to get almost the following working in my manager function:
void setFunc2(std::weak_ptr<MyBase> base, std::function<void(double)> func) {
m_function.first = base;
m_function.second = std::bind(func, base, std::placeholders::_1);
}
But the above gives errors:
error: no match for 'operator=' (operand types are 'std::function<void(double)>' and
'std::_Bind_helper<false, std::function<void(double)>&, std::weak_ptr<MyBase>&, const std::_Placeholder<1>&>::type {aka std::_Bind<std::function<void(double)>(std::weak_ptr<MyBase>, std::_Placeholder<1>)>}')
m_function.second = std::bind(func, base, std::placeholders::_1);
Is there a better way to do this, or perhaps a way to get this working?
Something interesting that I notice. If I use the std::shared_ptr
the use_count()
gets incremented with the call to std::bind
in the original code. Thus I can not manually reset/destroy the object unless I unset the member on my manager. Where is this behaviour documented, I normally use cppreference?
I have looked at the following question but can't seem to get it working for my problem: How can I use polymorphism with std::function?
Template setFunction
so that you can accept pointer-to-member-of-derived, and don't have to write 12 overloads for the combinations of cv/ref qualifiers.
template<class D, class D2, class F>
void setFunction(const std::shared_ptr<D> &sp, F D2::* member) {
// optionally static_assert that D2 is a base of D.
m_function.first = sp;
m_function.second = std::bind(member, sp.get(), std::placeholders::_1);
}
Obviously you need to make sure you lock()
m_function.first
before calling m_function.second
.
Alternatively, just use a lambda that captures both the weak_ptr
and the member function pointer:
std::function<void(double)> m_function;
template<class D, class D2, class F>
void setFunction(const std::shared_ptr<D> &sp, F D2::* member) {
std::weak_ptr<D> wp = sp;
m_function = [wp, member](double d) {
if(auto sp = wp.lock()){
((*sp).*member)(d);
}
else {
// handle pointer no longer valid case.
}
};
}