2

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.

peetonn
  • 2,942
  • 4
  • 32
  • 49
  • The cute answer is "well don't do that silly". – BlamKiwi Jan 22 '15 at 02:10
  • 7
    Um, if you use dangling pointers, you get what you get. What is your real question? If you're asking how to manage ownership better, the answer is to manage ownership better. – Lightness Races in Orbit Jan 22 '15 at 02:11
  • 1
    I asked this the other day: http://stackoverflow.com/questions/28054303/stdfunction-to-member-function-of-object-and-lifetime-of-object maybe it can help. – Alex Zywicki Jan 22 '15 at 03:02
  • @LightnessRacesinOrbit @MorphingDragon the real reason 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. I wish the library had something like `unregsiterCallbacks` call... – peetonn Jan 22 '15 at 07:42
  • @peetonn: Yeah pretty poor library if it doesn't. Do callbacks "stack", or does a newly-registered callback replace a previously-registered one? And is it safe to "register" a singular function pointer (one that doesn't point anywhere, like a "null pointer")? If so, there's your answer: `registerCallback(nullptr);` Though you also say you have "no control" over what's passed into the library. Why do you have a design that gives you so little control over anything? And who _does_ have control? I maintain that this is a design issue and patching around it is not optimal. – Lightness Races in Orbit Jan 22 '15 at 10:13
  • @LightnessRacesinOrbit it stacks callbacks, so registering a nullptr won't help, although a good idea in the first case. All what library call takes as an argument is a 'SomeFunc' object. It's a third-party lib, but I'll talk with the developer about this issue. – peetonn Jan 22 '15 at 17:17

4 Answers4

3

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
b4hand
  • 9,550
  • 4
  • 44
  • 49
  • 1
    Not sure why this got downvoted. Closures are typically the tool to make sure particular state lives long enough. – BlamKiwi Jan 22 '15 at 02:17
  • Downvote removed - in your last edit, you fixed your "dangling reference" bug. I'm sure you noticed. :) – Drew Dormann Jan 22 '15 at 02:49
1

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.

Siqi Lin
  • 1,237
  • 1
  • 10
  • 25
1

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

more details here and here.

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;
    }
};
Community
  • 1
  • 1
VinSmile
  • 756
  • 6
  • 10
  • thanks, @VinSmile. your solution #1 works perfectly for me - no need to hold an object for a function to be called. – peetonn Jan 22 '15 at 08:11
0

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.

peetonn
  • 2,942
  • 4
  • 32
  • 49