Jonathan Mee
Jonathan Mee

Reputation: 38919

Validate a Functor's Target Object

I have class foo that accepts a member function pointer to one of class bar's methods, but class bar may have shorter lifetime than foo is there a way that the Functor can check for bars existence before executing?

Currently, I'm trying to use std::function's bool operator without success.

class Foo
{
public:
    void Foo::SetCallback( std::function< void( void ) > cb )
    {
        _cb = cb; // My Bar class will call this to assign a Bar member function pointer to Foo's _cb member variable
    }
    void Foo::CallCallback()
    {
        if( _cb ) _cb(); // This evaluates to true even if the original bar object has been destroyed
        //I want this callback to only execute if the bar object exists
    }
private:
    std::function< void( void ) > _cb;
};
class Bar
{
public:
    Bar( Foo* _foo )
    {
        _foo->SetCallback( std::bind( &myCalledbackFunc, this );
    }
    void myCalledbackFunc()
    {
        //do stuff
    }
};

Upvotes: 0

Views: 186

Answers (2)

Jonathan Mee
Jonathan Mee

Reputation: 38919

In C++ the memory used by an object is not zeroed on destruction of the object. So as Silvester Seredi mentions in his answer the only way for any pointer to know whether the object that it references has been destroyed is for the pointer to reference an object owning auto-pointer. It is additionally notable that an object cannot be owned internally, so an object cannot intrinsically provide an indicator to whether it has been destroyed.

Therefore, the only solution is to notify the class holding the functor that it is invalid upon the destruction of the class the functor references a member of, as is suggested by perreal. Note that this is not multi-thread safe.

class Foo
{
public:
    void SetCallback( std::function< void( void ) > cb )
    {
        _cb = cb;
    }
    void UnSetCallback()
    {
        _cb = std::function< void( void ) >();
    }
    void CallCallback()
    {
        if( _cb ) _cb();
    }
private:
    std::function< void( void ) > _cb;
};
class Bar
{
public:
    Bar( Foo* foo ) : _foo( foo )
    {
        _foo->SetCallback( std::bind( &myCalledbackFunc, this );
    }
    ~Bar()
    {
        _foo->UnSetCallback();
    }
    void myCalledbackFunc()
    {
        //do stuff
    }
private:
    Foo _foo;
};

Upvotes: 0

Silvester
Silvester

Reputation: 421

I can't think of any simple way how to let Foo know when the callback object is destroyed, but if you really want to verify from the Foo class whether the object is alive, I'd use a weak_ptr somewhere along the road. If you are creating Bar through new you could instead create the instances through make_shared and you'd do something like this (the additional cost is one function call after every construction of a Bar instance):

class Bar;
class Foo
{
public:
    void Foo::SetCallback( std::function< void( void ) > cb )
    {
        _cb = cb;
    }

    void Foo::RegisterBar(std::weak_ptr<Bar> inbarPtr)
    {
        barRef = inbarPtr;
    }

    void Foo::CallCallback()
    {
        if( _cb && !barRef.expired()) _cb(); 
    }

private:
    std::function< void( void ) > _cb;
    std::weak_ptr<Bar> barRef;
};

class Bar
{
public:
    Bar( Foo* _foo )
    {
        _foo->SetCallback( std::bind( &Bar::myCalledbackFunc, this ));
    }
    void myCalledbackFunc()
    {
        //do stuff
    }
};

int main()
{
    Foo fooInstace;
    std::shared_ptr<Bar> barInstance = std::make_shared<Bar>(&fooInstace);
    fooInstace.RegisterBar(barInstance);
    return 0;
}

If you really insist on changes only to the Bar class, you could use this ugly, ugly hack with an empty custom deleter:

class Bar;
class Foo
{
public:
  void Foo::SetCallback( std::function< void( void ) > cb , std::weak_ptr<Bar> inbarPtr)
  {
    _cb = cb;
    barRef = inbarPtr;
  }
  void Foo::CallCallback()
  {
    if( _cb && !barRef.expired()) _cb(); 
  }
private:
  std::function< void( void ) > _cb;
  std::weak_ptr<Bar> barRef;
};


class Bar
{
  std::shared_ptr<Bar> selfPtr;
public:
  Bar( Foo* _foo )
  {
    selfPtr = std::shared_ptr<Bar>(this, Bar::EmptyDeleter);
    _foo->SetCallback( std::bind( &Bar::myCalledbackFunc, this ), selfPtr);
  }
  void myCalledbackFunc()
  {
    //do stuff
  }
protected:
  static void EmptyDeleter(Bar*)
  {
  }
};

Upvotes: 1

Related Questions