I have the following system of callbacks
class ... {
...
std::vector<std::function<void()>> systemBringUps;
std::vector<std::function<void()>> systemTearDowns;
std::vector<std::function<void(EntityID_t)>> systemMains;
std::vector<std::function<bool(EntityID_t)>> systemChecks;
template <typename T>
void registerSystem() {
systemBringUps.push_back(T::systemBringUp);
systemTearDowns.push_back(T::systemTearDown);
systemMains.push_back(T::systemMain);
systemChecks.push_back(T::systemCheck);
T::onSystemRegister();
}
template <typename T>
void deregisterSystem() {
std::function<void()> bringUp = T::systemBringUp;
std::function<void()> tearDown = T::systemTearDown;
std::function<void(EntityID_t)> smain = T::systemMain;
std::function<bool(EntityID_t)> check = T::systemCheck;
std::remove(systemBringUps.begin(), systemBringUps.end(), bringUp);
std::remove(systemTearDowns.begin(), systemTearDowns.end(), tearDown);
std::remove(systemMains.begin(), systemMains.end(), smain);
std::remove(systemChecks.begin(), systemChecks.end(), check);
T::onSystemDeregister();
}
The registerSystem
template function works fine, but the deregisterSystem
function fails to compile, stating
...
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/c++/7.5.0/bits/predefined_ops.h:241:17: error: invalid operands to binary expression
('std::function<bool (unsigned short)>' and 'const std::function<bool (unsigned short)>')
{ return *__it == _M_value; }
~~~~~ ^ ~~~~~~~~
...
So it seems that std::remove
cannot compile since the std::function
s in deregisterSystem
that I am defining are const
, whereas those in the vectors I am trying to remove from are not? Is there any way I can discard the const
qualifier from these std::function
s? I have tried using the copy constructor as in
template <typename T>
void deregisterSystem() {
std::function<void()> bringUp(T::systemBringUp);
std::function<void()> tearDown(T::systemTearDown);
std::function<void(EntityID_t)> smain(T::systemMain);
std::function<bool(EntityID_t)> check(T::systemCheck);
std::remove(systemBringUps.begin(), systemBringUps.end(), bringUp);
std::remove(systemTearDowns.begin(), systemTearDowns.end(), tearDown);
std::remove(systemMains.begin(), systemMains.end(), smain);
std::remove(systemChecks.begin(), systemChecks.end(), check);
T::onSystemDeregister();
}
but this fails in a similar way.
Your problem is not constness but that that std::function
objects can not be compared for equality at all. Your current approach simply will not work.
The solution is to give some sort of unique 'token' during registration to the caller which can be used to unregister later.
You can use std::function<F>::target
to get the held function object directly and compare that.
For example, to erase all of the functions that are T::systemBringUp
in systemBringUps
, you could do something like:
systemBringUps.erase(std::remove_if(systemBringUps.begin(), systemBringUps.end(), [](const auto& f) {
auto* target = f.template target<decltype(T::systemBringUp)>();
return target != nullptr && *target == T::systemBringUp;
}), systemBringUps.end());
You can make a helper function to check if a std::function
is holding a value:
template<class F, class T>
bool is_function_holding(const std::function<F>& func, const T& target) {
static_assert(std::is_assignable<std::function<F>&, const T&>::value, "Function could not possibly hold target");
auto* current_target = func.template target<typename std::decay<T>::type>();
return current_target != nullptr && *current_target == target;
}
// Later
systemBringUps.erase(std::remove_if(systemBringUps.begin(), systemBringUps.end(), [](const auto& f) {
return is_function_holding(f, T::systemBringUp);
}, systemBringUps.end());
systemTearDowns.erase(std::remove_if(systemTearDowns.begin(), systemTearDowns.end(), [](const auto& f) {
return is_function_holding(f, T::systemTearDown);
}, systemTearDowns.end());
// ...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With