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
и использование исходных итераторов для отмены регистрации представляется мне самым простым.