Define std::hash<std::function>
Asked Answered
B

1

6

I need to create a templated class that can hold pointers to elements of type T and then performs functions on them. The functions will come from different places, so I need a container to store them, so I can call them later. I decided to use an std::unordered_set, because it offers speed and restricts duplication due to it being implemented as a hash table. I have a whole class written, but it doesn't compile due to there not being a hash function defined for my std::function that takes a pointer of type T and returns void. It's easy enough to specify it with struct hash<std::function<void(MyCustomType*)>> (and overloading the () operator, too) for each type I use, but how do I actually hash the function?

Here is a watered-down excerpt from my class with the relevant members and methods:

template <typename T>
class Master {
private:
    std::unordered_set<std::function<void(T*)>> functions;
protected:
    registerFunction(std::function<void(T*)> function) {
        this->functions.insert(function);
    }
    unregisterFunction(std::function<void(T*)> function) {
        this->functions.erase(function);
    }
};

I'm not completely bound to using an std::unordered_set, but it seems to offer everything that I'd need to get this piece (and the rest of my code) working well.

Am I thinking about this the wrong way? Is it completely impossible to hash a std::function?

Brodsky answered 19/12, 2013 at 22:17 Comment(3)
You would also need to define some op==, which is about impossible to do.Haase
The problem you face is that a std::function could hold any functor of any compatible type. That's the whole point of it, type erasure. Without access to that thing it contains, you can't hash it (or compare values). And even if std::function wanted to give you access it basically can't: you can't write a function that does anything with it because the type could be anything.Marrin
Revisiting this problem in my head several years later, the appropriate solution is to generate an ID upon registration and make the caller provide the ID when unregistering. And if uniqueness truly is needed, make the caller provide the ID for the registration as well, because uniqueness of the function shouldn't be a concern of the register/unregister class. I think I was just overengineering it anyways. Especially since this code has been in use for several years now and has never even needed the unregistration...Brodsky
E
3

A set is mostly something you will check that data is in it.

So I do not see the point of using one here... You'll have your functions and you'll store them in the set, and after that, what ? You just iterate on them ?

For your question, a element of a set should have a way to generate a hash and an operator==(). The second is not provided for std::function and thus you wouldn't be able to check that your function is really in the set.

So even if you find a way to generate an hash from the function, you would be stuck... And I do not see how to meet the hash requirement.

Why not simply use a std::vector ?

Esp answered 19/12, 2013 at 22:27 Comment(5)
I thought about switching to a std::vector, and that's what I'll probably do now after hearing this. I just won't get the uniqueness that I was hoping for. I'd just have to return the index in the registerFunction() method for use in unregisterFunction(). Thanks!Brodsky
Returning the index won't work correctly, as they can be invalidated when you erase an element in the vector. You could switch to a std::list and return iterators or store dummy functions in the vector when elements are removed.Toothpick
@AndrewLarsson Even with a registering mecanism I do not see how you will prevent double call. If you really want some sort of unicity control, why not forcing function naming with a std::unordered_map<std::string, std::function<XXX>> ? The user of your code would have to register its function with two different names and that would involve double call points.You may even use some infamous macro to associate the name to the function automagically.Esp
@Esp I guess for now I'll just have to trust the user to only register their functions once, as it seems that it's more work than it's worth as it stands, but the std::unordered_map idea would work quite nicely!Brodsky
I ended up using std::list<std::function<void(std::shared_ptr<T>)>> functions; and took out the ability to unregister functions.Brodsky

© 2022 - 2024 — McMap. All rights reserved.