2

I am currently experimenting with writing an event queue in C++11. I am using std::bind to obtain std::function objects which are called when certain events happen. The code for this roughly looks like this:

class A
{
public:
  void handle();
};

class B { ... };

// Later on, somewhere else...
std::vector< std::function< void() > functions;

A a;
B b;

functions.push_back( std::bind( &A::handle, &a ) );
functions.push_back( std::bind( &B::handle, &b ) );

// Even later:
for( auto&& f : functions )
  f(); // <--- How do I know whether f is still "valid"?

Is there any way to guarantee the validity of the function object so that I can avoid stumbling over undefined behaviour here?

I have already taken a look at this question here, std::function to member function of object and lifetime of object, but it only discussed whether deleting a pointer to a bound object raises undefined behaviour. I am more interested in how to handle the destruction of such an object. Is there any way to detect this?

EDIT: To clarify, I know that I cannot guarantee a lifetime for non-static, non-global objects. It would be sufficient to be notified about their destruction so that the invalid function objects can be removed.

Community
  • 1
  • 1
Gnosophilon
  • 1,340
  • 7
  • 24
  • 2
    A member function doesn't have a "lifetime", the object you use when binding the member function, on the other hand, do have a lifetime, and it's that objects lifetime you have to think about. And unfortunately there is no way to *guarantee* the lifetime of non-static and non-global objects. – Some programmer dude Apr 21 '15 at 07:47
  • Yes, you are right. Is it possible to be notified about the destruction of non-static, non-global objects? Or should I rely on the respective class instances to handle the removal of the function objects upon deletion themselves? – Gnosophilon Apr 21 '15 at 07:50

2 Answers2

4

As @Joachim has stated, no lifetime is associated to the member function (it's a code section, not data). So you're asking if there is a way to know if the object still exists prior to execute the callback call.

You've to make a sort of framework, where the object dctor notify the container when it is destroyed, so the container could delete it from its "observers", the vector containing all the objects. To do that, the object must memorize in its instance the ptr to the container.

UPDATE

@Jason talks about the use of shared_ptr. It's okay to use them, but in this case, is not addressing the case of HOW to destroy the object linked in other object-notification list. Shared_ptr postponed the destruction of an instance until all "managed" references to it are deleted. But if you need to destroy object A, AND delete all reference to it because that object MUST be deleted, you've to look into all containers that store a shared_ptr and remove it. A very painful activity. The simplest solution (using raw ptr or shared_ptr, if you can use them, is irrelevant) is a two-link connection between the observer and the observed, in such way each one can notify its destruction to the other. How to store this information? many ways to accomplish it: hash tables, slots in observer, etc

Mouze
  • 706
  • 5
  • 10
  • Thank you for the answer. I am going to manage the connection using an auxiliary object now. I just thought it worthwhile to discuss potential alternatives because my solution did not strike me as very elegant. – Gnosophilon Apr 23 '15 at 07:12
0

One hack/workaround that achieves the desired result would be to use a parameter of type std::shared_ptr. When the bind is destructed, so is the shared pointer - which will do the right thing when it is the last reference. However this involves changes to the signature used. To make it slightly less awkward, you can use static methods that take in a std::shared_ptr this - sort of like the self parameter concept in python, if you are familiar.

Or if you are fine with C++11, you can just use a lambda capture of the shared pointer.

You'd need to dynamically allocate the instances to use this method.

Jason Newton
  • 1,201
  • 9
  • 13
  • This is not the question asked: if you put a shared_ptr, you're not addressing the case of object is destroyed; the object is not destroyed and the "observable" still emits notifications. You have to manually iterate and delete all references to the deleting object, it's very unpractical. – Mouze Apr 21 '15 at 12:21
  • Could you elucidate about the lambda capture? I am not sure what you mean by this. – Gnosophilon Apr 21 '15 at 14:10
  • @Mouze - you have to go shared_ptr all the way for these types of objects. The object cannot be destroyed while a valid shared_ptr instance exists. When, in the example, the functions list drops the std::function with the std::bind result, and no other shared_ptr's exist, only then will that object delete. – Jason Newton Apr 22 '15 at 00:27
  • @Gnosophilon: you must still use shared_ptrs in the lambda approach but it makes it so you don't have to use methods taking "this" as a parameter: `[shared_this](){ shared_this->handle(); } ` Also noting std::enable_shared_from_this – Jason Newton Apr 22 '15 at 00:38
  • @Jason shared_ptr is not the panacea for all cases. In this case, is useless for the asked question: you must address the case when the application decide to destroy an object, if or not the object is present in another shared_ptr; for example, if you have the same object in 2 observed-list, and you execute to delete shared_ptr_to_object, you really didn't nothing, but only decrease the counter. The object is still alive, still receiving the notification, and it's not the behaviour desidered. As I stated, you need an unregister() function in the observer, that store 1..N ptr/shared_ptr – Mouze Apr 22 '15 at 07:50
  • 1
    @Mouze the op's question has changed a little temporally. The original (and still valid) question as I read it is to guarantee the callback can always be executed with a valid object and shared_ptr's solve that problem as the container of function objects now keeps those objects alive. Managing a container of registered callbacks is a different problem solvable in a variety of ways, such as ticket-ed iterators from a linked list returned on "registration". Boost.Asio regularly demonstrates the shared_this pattern with a very similar problem, take a peak at their asynchronous tutorials. – Jason Newton Apr 22 '15 at 08:21
  • @Jason Thanks Jason to let me know about Boost.Asio. I will take a look at it. I agree we are shifting to a more complex solution, but however you must agree that "It would be sufficient to be notified about their destruction " means exactly this :) – Mouze Apr 22 '15 at 08:28