简体   繁体   中英

C++ removing an std::function from a std::vector<std::function<…>>

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());
// ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM