Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::function as callback, is unregistration possible?

Tags:

c++

c++11

The question is strictly about std::function and not boost::function. See the Update section at the bottom of this question for more details, especially the part about it not being possible to compare non-empty std::function objects per the C++11 standard.


The C++11 std::function class template is great for maintaining a collection of callbacks. One can store them in a vector, for example and invoke them when need be. However, maintaining these objects and allowing for unregistration seems to be impossible.

Let me be specific, imagine this class:

class Invoker
{
public:
  void Register(std::function<void()> f);
  void Unregister(std::function<void()> f);

  void InvokeAll();

private:
  // Some container that holds the function objects passed to Register()
};

Sample usage scenario:

void foo()
{
}

int main()
{
  std::function<void()> f1{foo};
  std::function<void()> f2{[] {std::cout << "Hello\n";} };

  Invoker inv;

  // The easy part

  // Register callbacks
  inv.Register(f1);
  inv.Register(f2);

  // Invoke them
  inv.InvokeAll();

  // The seemingly impossible part. How can Unregister() be implemented to actually
  // locate the correct object to unregister (i.e., remove from its container)?
  inv.Unregister(f2);
  inv.Unregister(f1);
}

It is fairly clear how the Register() function can be implemented. However, how would one go about implementing Unregister(). Let's say that the container that holds the function objects is vector<std::function<void()>> . How would you find a particular function object that is passed to the Unregister() call? std::function does supply an overloaded operator==, but that only tests for an empty function object (i.e., it cannot be used to compare two non-empty function objects to see if they both refer to the same actual invocation).

I would appreciate any ideas.

Update:

Ideas so far mainly consist of the addition of a cookie to be associated with each std::function object that can be used to unregister it. I was hoping for something that is not exogenous to the std::function object itself. Also, there seems to be much confusion between std::function and boost::function. The question is strictly about std::function objects, and not boost::function objects.

Also, you cannot compare two non-empty std::function objects for equality. They will always compare non-equal per the standard. So, links in the comments to solutions that do just that (and use boost::function objects to boot) are patently wrong in the context of this question.

like image 781
Michael Goldshteyn Avatar asked Apr 10 '13 03:04

Michael Goldshteyn


People also ask

What is a register callback?

The RegisterCallback function is called by a user-mode security package. A security package loaded into the Local Security Authority (LSA) process can call a registered user-mode callback function using the ClientCallBack function available in the LSA_SECPKG_FUNCTION_TABLE structure.

How do you call a callback function in C++?

In simple language, If a reference of a function is passed to another function as an argument to call it, then it will be called as a Callback function. In C, a callback function is a function that is called through a function pointer. In C++ STL, functors are also used for this purpose.

When callback function is executed?

A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action. The above example is a synchronous callback, as it is executed immediately.

Is callback a function?

A callback is a function passed as an argument to another function.


1 Answers

Since you can't test for element identity in the container, it's probably best to use a container (such as std::list) whose iterators do not invalidate when the container is modified, and return iterators back to registering callers that can be used to unregister.

If you really want to use vector (or deque), you could return the integral index into the vector/deque when the callback is added. This strategy would naturally require you to make sure indexes are usable in this fashion to identify the function's position in the sequence. If callbacks and/or unregistration is rare, this could simply mean not reusing spots. Or, you could implement a free list to reuse empty slots. Or, only reclaim empty slots from the ends of the sequence and maintain a base index offset that is increased when slots are reclaimed off the beginning.

If your callback access pattern doesn't require random access traversal, storing the callbacks in a std::list and using raw iterators to unregister seems simplest to me.

like image 110
Adam H. Peterson Avatar answered Nov 06 '22 11:11

Adam H. Peterson