удалить любой элемент вектора>, связанный с функцией-членом
Как удалить функцию, которая связана с функцией-членом объекта this
:
std::vector<std::function<void(int)>> callbacks;
class MyClass {
public:
MyClass() {
callbacks.push_back(
std::bind(&MyClass::myFunc,this,std::placeholders::_1)
);
}
~MyClass() {
auto it = std::remove_if( std::begin(callbacks),
std::end(callbacks),
[&](std::function<void(int)>& f) {
return // <-- this is my question
// true (remove) if f is bound to member function
// of this
});
callbacks.erase(it,std::end(callbacks));
}
void myFunc(int param){...}
};
3 ответа:
В общем случае вы не можете обойтись без дополнительной работы. Тип erasure удаляет эту информацию из объекта, и
std::function
не предоставляет эту информацию напрямую.Ваш конкретный пример может иметь только одну функцию-член, которая может быть кандидатом на удаление, но как насчет класса с 5 членами, которые могут храниться как обратные вызовы? Вам нужно будет протестировать их все, а также можно связать функции-члены с помощью лямбды, что довольно много необнаруживаемый.
Вот одно решение, если:
- все обратные вызовы регистрируются изнутри
MyClass
- контейнер изменен для хранения дополнительной информации
- вы готовы взять на себя всю дополнительную бухгалтерию
std::vector<std::pair<std::function<void(int)>, void*>> callbacks; class MyClass{ static unsigned const num_possible_callbacks = 2; // keep updated std::array<std::type_info const*, num_possible_callbacks> _infos; unsigned _next_info; // adds type_info and passes through template<class T> T const& add_info(T const& bound){ if(_next_info == num_possible_callbacks) throw "oh shi...!"; // something went out of sync _infos[_next_info++] = &typeid(T); return bound; } public: MyClass() : _next_info(0){ using std::placeholders::_1; callbacks.push_back(std::make_pair( add_info(std::bind(&MyClass::myFunc, this, _1)), (void*)this)); callbacks.push_back(std::make_pair( add_info([this](int i){ return myOtherFunc(i, 0.5); }), (void*)this)); } ~MyClass(){ using std::placeholders::_1; callbacks.erase(std::remove_if(callbacks.begin(), callbacks.end(), [&](std::pair<std::function<void(int)>, void*> const& p) -> bool{ if(p.second != (void*)this) return false; auto const& f = p.first; for(unsigned i = 0; i < _infos.size(); ++i) if(_infos[i] == &f.target_type()) return true; return false; }), callbacks.end()); } void myFunc(int param){ /* ... */ } void myOtherFunc(int param1, double param2){ /* ... */ } };
typedef decltype(std::bind(&MyClass::myFunc,this,std::placeholders::_1)) bound_type; auto it = std::remove_if( std::begin(callbacks), std::end(callbacks), [](const std::function<void(int)>& f) { return f.target<bound_type>() != nullptr; });
Шаблон функции-члена
std::function::target<T>
возвращает указатель на целевой объект, если он имеет типT
, в противном случае он возвращает null. Поэтому вам просто нужно иметь возможность назвать тип целевого объекта, который вы можете получить изdecltype
. Довольно просто на самом деле: -)N. B. который удалит любые обратные вызовы этого типа, а не только те, которые связали указатель
this
для конкретного уничтожаемого объекта. Если вы пытаетесь предотвратить вызов обратных вызовов для объекта после него если вы не можете определить, какие элементы вектора относятся к каким объектам, вы можете поместить shared_ptr в свой класс, а затем сохранить weak_ptr для него в обратном вызове, который может быть использован для обнаружения, если объект был уничтожен:class MyClass { struct NullDeleter { void operator()(void*) const { } }; std::shared_ptr<MyClass> sp; static void safe_invoke(void (MyClass::*f)(int), const std::weak_ptr<MyClass>& wp, int i) { if (std::shared_ptr<MyClass> safe_this = wp.lock()) (safe_this.get()->*f)(i); } public: MyClass() : sp(this, NullDeleter()) { callbacks.push_back( std::bind(safe_invoke, &MyClass::myFunc ,std::weak_ptr<MyClass>(sp), std::placeholders::_1) ); };
Это оборачивает вызов функции-члена с функцией
invoke
, которая преобразуетweak_ptr
вshared_ptr
перед вызовом функции-члена. Если объект был уничтожен, тоshared_ptr
будет пустым, так что функция ничего не делает. Это фактически не удаляет обратный вызов, когда он становится недействительным, но делает его безопасным для вызова.