0

I have a class X and I want to pass a member function bar to another class Y, which will store it in a vector and invoke it at some other time.

I understand that I need to make sure that X still exists when Y invokes the function. How do I do that?

In my example below, I initially thought that if I passed a shared_ptr to bar this would do what I want - i.e. as long as Y exists, then so will X, as Y holds a shared_ptr to one of its member functions.

I'm pretty sure now that this logic is incorrect. Can someone please enlighten me?

class Y {
  std::vector<std::shared_ptr<std::function<void()>>> m_v;
public:
  void foo(const std::shared_ptr<std::function<void()>>& callback) {
    m_v.push_back(callback);
  }

  void sometime_later() {
    // invoke call back function in vector
    // how do I ensure the object I want to call the function on still exists
  }
};

class X {
  Y& m_y;
public:
  X(Y& y) : m_y(y) {
    m_y.foo(std::make_shared<std::function<void()>>(std::bind(&X::bar, this)));
  }

  void bar() {
    // do some stuff
  }
};
101010
  • 41,839
  • 11
  • 94
  • 168
ksl
  • 4,519
  • 11
  • 65
  • 106
  • Couldnt you use more expressive names than foo , bar, X and Y to make the code more readable and the idea more understandable? – A.S.H Nov 05 '15 at 20:19
  • You are correct, holding a `shared_ptr` to a `std::function` to which a member function of an `X` has been bound has no influence on the lifetime of the `X` instance at all. Without some context of what you're ultimately trying to do it's a bit difficult to recommend a solution. – melak47 Nov 05 '15 at 20:25
  • I don't think that the vector of `std::shared_ptr` will do what you want. You are sharing a `std::function` and not instances of `X`. You should think about what `std::shared_ptr` will delete when it gets deleted itself. – quamrana Nov 05 '15 at 20:25
  • @A.S.H I think the idea is pretty understandable as it is. The question is clear. – ksl Nov 06 '15 at 07:48

2 Answers2

0

Here is an implementation using std::enable_shared_from_this in conjunction with shared_ptr's aliasing constructor:

#include <vector>
#include <iostream>
#include <memory>
#include <functional>

class Y {
    std::vector<std::shared_ptr<std::function<void()>>> m_v;
public:
    void foo(const std::shared_ptr<std::function<void()>>& callback) {
      m_v.push_back(callback);
    }

    void sometime_later(){
        for (const auto& f: m_v) {
            (*f)();
        }
    }
};

class X : public std::enable_shared_from_this<X> {
  Y& m_y;
  std::function<void()> m_callback;
public:
  X(Y& y) : m_y(y), m_callback(std::bind(&X::bar, this)) {
  }

  void add()
  {
    m_y.foo(std::shared_ptr<std::function<void()>>{shared_from_this(), &m_callback});
    std::cout << shared_from_this().use_count() << "\n";
  }

  void bar() {
      std::cout << shared_from_this().use_count();
  }
};
int main(){

    Y y;
    {
        auto x = std::make_shared<X>(y);
        x->add();
    }
    y.sometime_later();
}

LIVE

Rostislav
  • 3,857
  • 18
  • 30
0

I ended up changing the design to resolve my lifetime issue.

Rather than declaring a vector as follows:

std::vector<std::shared_ptr<std::function<void()>>> 

and issuing a call on the stored function sometime later, I declare it as:

std::vector<std::shared_ptr<X>> 

and then place a requirement on the object X to implement a named function (in my example above it woyuld be bar) and then call it at the appropriate time. That way I ensure X exists when the callback function is invoked.

ksl
  • 4,519
  • 11
  • 65
  • 106