Std:: function as callback, возможна ли регистрация?

Вопрос строго о std::function, а не boost::function. Дополнительную информацию см. В разделе "Обновление" в нижней части этого вопроса, особенно в том, что в нем нет возможности сравнивать непустые объекты std::function по стандарту С++ 11.


Шаблон класса С++ 11 std::function отлично подходит для поддержки набора обратных вызовов. Например, можно хранить их в vector и вызывать их, когда это необходимо. Однако сохранение этих объектов и разрешение на регистрацию представляется невозможным.

Позвольте мне указать, представьте себе этот класс:

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()
};

Пример использования сценария:

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);
}

Довольно ясно, как можно реализовать функцию Register(). Однако как бы реализовать реализацию Unregister(). Скажем, что контейнер, содержащий объекты функции, vector<std::function<void()>>. Как вы найдете конкретный объект функции, который передается в вызов Unregister()? std::function действительно содержит перегруженный operator==, но это только тесты для пустого объекта функции (т.е. он не может использоваться для сравнения двух непустых объектов функции, чтобы увидеть, ссылаются ли они на один и тот же фактический вызов).

Буду признателен за любые идеи.

Update:

Идеи до сих пор в основном состоят из добавления файла cookie, который должен быть связан с каждым объектом std::function, который можно использовать для его регистрации. Я надеялся на то, что не является экзогенным для самого объекта std::function. Кроме того, существует много путаницы между std::function и boost::function. Вопрос строго о объектах std::function и не boost::function.

Кроме того, вы не можете сравнивать два непустых объекта std::function для равенства. Они всегда будут сравнивать не равные по стандарту. Таким образом, ссылки в комментариях к решениям, которые делают именно это (и использовать объекты boost::function для загрузки), явно противоречат этому вопросу.

Ответ 1

Поскольку вы не можете проверить идентификатор элемента в контейнере, лучше всего использовать контейнер (например, std::list), итераторы которого не будут аннулированы при изменении контейнера и возвратят итераторы назад для регистрации вызывающих абонентов, которые могут используется для отмены регистрации.

Если вы действительно хотите использовать vector (или deque), вы можете вернуть интегральный индекс в вектор /deque при добавлении обратного вызова. Эта стратегия, естественно, потребует от вас убедиться, что индексы можно использовать таким образом, чтобы идентифицировать положение функции в последовательности. Если обратные вызовы и/или отмена регистрации являются редкими, это может означать не повторное использование пятен. Или вы можете реализовать бесплатный список для повторного использования пустых слотов. Или только возвращайте пустые слоты с концов последовательности и поддерживайте смещение базового индекса, которое увеличивается, когда слоты возвращаются с начала.

Если ваш шаблон обратного вызова не требует случайного доступа, сохранение обратных вызовов в std::list и использование исходных итераторов для отмены регистрации представляется мне самым простым.