MichaelMitchell
MichaelMitchell

Reputation: 1167

Is it possible to detect if the object that a bound member function refers to is deleted or destroyed

I am exploring the use of std::function and std::bind. I see that you can bind a member function, for example:

class A{
    int c_ = 10;
public:
    int add(int a, int b){
        return a + b + c_;
    }
};
int main(){
    A* p_a = new A;
    std::function<int()> f = std::bind(&A::add, p_a, 1, 1);
    printf("%i\n", f()); // yields "12" (1 + 1 + 10)
    delete p_a;
    printf("%i\n", f()); // yields derpy numbers, no errors thrown.
}

Is there a way to detect if p_a has been deleted?

My solution to do this is having a wrapper class that holds the function and a weak_ptr to the object. I am just wondering if there is a more elegant way to do this.

Upvotes: 2

Views: 896

Answers (2)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275740

struct A{
  int c_ = 10;
  int add(int a, int b){
    return a + b + c_;
  }
};
template<class T>
std::weak_ptr<T> weak( std::shared_ptr<T> const& sp ) { return {sp}; }

int main(){
  auto p_a = std::make_shared<A>();
  std::function<int()> f = [w_a = weak(p_a)]() {
    if (auto p_a = w_a.lock())
      return p_a->add(1,1);
    throw w_a;
  }
  printf("%i\n", f()); // yields "12" (1 + 1 + 10)
  p_a.reset();
  try {
    printf("%i\n", f()); // yields derpy numbers, no errors thrown.
  } catch( std::weak_ptr<A> wp ) {
    printf("object deleted\n");
  }
}

live example.

in general, in C++ you don't pay for what you don't use.

Tracking the lifetime of objects has a cost. If you want to track the lifetime of objects, you can use a shared_ptr or weak_ptr to the (free-store) allocated object, or use a weak_ptr to a shared_ptr (uniquely) owned by the object to indicate its lifetime is over.

The above is an implementation using C++14 lambdas to capture the object's shared pointer as a weak ptr, and give defined behavior (a throw of a copy of said weak pointer) if it has been deleted.

A lifetime token looks like:

using lifetime_token = std::weak_ptr<void>;
struct has_lifetime {
  has_lifetime():token(std::make_shared<char>()) {}
  has_lifetime(has_lifetime const&o):has_lifetime() {} // not default
  lifetime_token get_lifetime() const {
    return token;
  }
private:
  std::shared_ptr<void> token;
};

inheriting from has_lifetime gives you a get_lifetime() member that exists for as long as you do (it is destroyed by your destructor, and can no longer be .lock()d).

This is an easier pattern to follow if you cannot modify the ownership semantics of the original class. Simply .lock() the lifetime_token of the object to determine if it is still alive.

Upvotes: 3

lisyarus
lisyarus

Reputation: 15568

std::bind can accept smart pointers, so you can simply pass std::shared_ptr<A> to it.

std::shared_ptr<A> p_a(new A);
std::function<int()> f = std::bind(&A::add, p_a, 1, 1);

Note, that the functor will own the object: the object will live as long as the functor lives. If you don't want such behavior, then your solution with a weak_ptr wrapper is nice.

Upvotes: 5

Related Questions