Reputation: 3052
Consider this code:
#include <iostream>
#include <functional>
using namespace std;
using namespace std::placeholders;
typedef function<void(const int&)> SomeFunc;
class X {
public:
X(string name):name_(name)
{ cout << "ctor " << name_ << endl; }
~X()
{
cout << "dtor " << name_ << endl;
name_ = "empty";
}
SomeFunc
getSomeFunc()
{ return bind(&X::someMethod, this, _1); }
private:
string name_;
void
someMethod(const int& a)
{
cout << name_ << " some method with " << a << endl;
}
};
int main()
{
SomeFunc f;
{
shared_ptr<X> x(new X("Object"));
f = x->getSomeFunc();
f(1);
}
f(2);
return 0;
}
Sometimes, output gives me this:
ctor Object
Object some method with 1
dtor Object
empty some method with 2
other times this:
ctor Object
Object some method with 1
dtor Object
some method with 2
In real world, it would most probably give me crashes once deallocated object tries to access it's attributes. So here is a question - as function does not guarantee holding a reference to the object which method it's pointing to, what is the best practice to avoid crashes when function is called after referenced object was already deallocated?
One of the solutions I might think of - maintain a special flag bool deallocated_
inside object and check it inside the method which might be called after deallocation. However, I suspect, it's not reliable either.
UPDATE (from comments):
The real reason I need this workaround is the library that takes function as a parameter. This library operates asynchronously and I have no control over function objects passed into it. That's why when my object is deallocated, library still can invoke callbacks using originally passed function which leads to a crash.
Upvotes: 2
Views: 626
Reputation: 3052
Another solution without using lambda is to derive from enable_shared_from_this
and pass shared_from_this
in getSomeFunc
method:
class X : public enable_shared_from_this<X> {
public:
X(string name):name_(name)
{ cout << "ctor " << name_ << endl; }
~X()
{
cout << "dtor " << name_ << endl;
name_ = "empty";
}
SomeFunc
getSomeFunc()
{
return bind(&X::someMethod, shared_from_this(), _1);
}
private:
string name_;
void
someMethod(const int& a)
{
cout << name_ << " some method with " << a << endl;
}
};
This, however, will hold object until all callbacks are released.
Upvotes: 0
Reputation: 756
Sulution 1) Using weak_ptr + lambda (almost the same as from b4hand, but it won't force your class beeing alive)
Inherit your class from std::enable_shared_from_this
class X : public enable_shared_from_this<X>
and change getSomeFunc to something like this:
SomeFunc getSomeFunc()
{
weak_ptr<X> weak = shared_from_this();
return [weak, this](const int& a){
shared_ptr<X> shared = weak.lock();
if (shared)
{
this->someMethod(a);
}
};
}
output:
ctor Object
Object some method with 1
dtor Object
Solution 2) A bit of crazy code + lambda
If you can't or don't want to use shared/weak ptrs, you can do it this way:
#include <memory>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include <set>
using namespace std;
typedef function<void(const int&)> SomeFunc;
class X {
private:
static set<X*> _aliveInstanties;
public:
X(string name) :name_(name)
{
_aliveInstanties.insert(this);
cout << "ctor " << name_ << endl;
}
~X()
{
_aliveInstanties.erase(_aliveInstanties.find(this));
cout << "dtor " << name_ << endl;
name_ = "empty";
}
SomeFunc getSomeFunc()
{
return [this](const int& a)
{
if (_aliveInstanties.find(this) != _aliveInstanties.end())
{
this->someMethod(a);
}
};
}
private:
string name_;
void someMethod(const int& a)
{
cout << name_ << " some method with " << a << endl;
}
};
Upvotes: 1
Reputation: 9770
Your object is being held by a shared_ptr
, so you can use a lambda to close over the shared_ptr
:
auto func = [ptr](const int &p){ ptr->someMethod(p); };
You'll need to use shared_from_this
to get ptr
within the class.
Here's a full example that works:
#include <iostream>
#include <functional>
#include <memory>
using namespace std;
using namespace std::placeholders;
typedef function<void(const int&)> SomeFunc;
class X : public enable_shared_from_this<X> {
public:
X(string name) : name_(name) {
cout << "ctor " << name_ << endl;
}
~X() {
cout << "dtor " << name_ << endl;
name_ = "empty";
}
SomeFunc getSomeFunc() {
auto ptr = shared_from_this();
return [ptr](const int &a){ ptr->someMethod(a); };
}
private:
string name_;
void someMethod(const int& a) {
cout << name_ << " some method with " << a << endl;
}
};
int main()
{
SomeFunc f;
{
shared_ptr<X> x(new X("Object"));
f = x->getSomeFunc();
f(1);
}
f(2);
return 0;
}
The output looks like this:
ctor Object
Object some method with 1
Object some method with 2
dtor Object
Upvotes: 3
Reputation: 1257
You can create a class that holds a function pointer and a shared_ptr
to the object. The shared_ptr
to the object guarantees the object won't be destroyed until your function class is destroyed.
Upvotes: 1