0

Is it possible to have a set(or unordered_set ) of functions in c++ like this?

set<function<void(int)>> funcSet;

I got something like this

error: invalid operands to binary expression
  ('const std::__1::function<void (int)>' and 'const std::__1::function<void (int)>')
    {return __x < __y;}    

How can I compare two functions?

mike
  • 25
  • 4
  • What are you trying to do? Compare the result of two void functions? How are they supposed to be ordered in the set? –  Nov 28 '14 at 00:59
  • @KerrekSB: then you are not aware of what std::function is. – v.oddou Nov 28 '14 at 01:02
  • 3
    @mike: perhaps you want to revisit your idea by rather creating a `map` (or `unordered_map`) of `string` to `std::function` ? related question: http://stackoverflow.com/questions/7624017/c0x-storing-any-type-of-stdfunction-in-a-stdmap – v.oddou Nov 28 '14 at 01:04
  • How should this behave: `funcSet.insert([](int){}); funcSet.insert([](int){});` One element or two? – Kerrek SB Nov 28 '14 at 01:09
  • To make a set you have to be able to order or hash the functions, but this isn't really possible for a generic `std::function`. I'd suggest using a vector instead. – M.M Nov 28 '14 at 01:10
  • After running through a few use cases I deleted my proposed solution. Using `target` and/or `target_type` member functions of `std;;function` is limited to basic function pointers. I agree with @v.oddou and think your best bet is going to be `std::map` or `std::unordered_map` – Captain Obvlious Nov 28 '14 at 02:00

2 Answers2

3

A set has the requirement that its elements may be ordered using <. So to put functions in a set, you have to define an ordering for functions first. For example, this comparison considers all functions with the same type as equal:

#include <set>
#include <functional>

using namespace std;

typedef function<void(int)> fun;

bool operator<(const fun& f1, const fun& f2) {
    return f2.target_type().name() < f2.target_type().name();
}

int main() {
    set<fun> fset;
}

Likewise, for an unordered_set, you'd have to define a specialization of std::hash<fun>.

Edit: I've borrowed the target idea from another solution to make the comparison well-defined.

Edit2: The most meaningful comparison for arbitrary functions would probably look like this:

struct fun_comp {
    template<typename Fun1, typename Fun2>
    bool operator()(const Fun1& f1, const Fun2& f2) {
        const char* c1 = f1._M_functor._M_pod_data;
        const char* c2 = f2._M_functor._M_pod_data;
        size_t sz = sizeof(f1._M_functor._M_pod_data);
        return lexicographical_compare(c1, c1+sz, c2, c2+sz);
    }
};

This is, obviously, completely unportable, depends on libstdc++-internals and will only compile with -fno-access-control, so you probably shouldn't actually do it like this.

Benno
  • 5,288
  • 5
  • 42
  • 60
  • 1
    This `operator<` definition is invalid , it must be a strict weak ordering but it relies on where the `set` is storing the objects! I guess you meant to try and compare the underlying function pointer, but that is problematic also. – M.M Nov 28 '14 at 01:08
  • Yeah, I'm not saying it's a good idea to do this, I'm trusting OP to have a valid reason for creating a set of functions and a sensible way to define the comparison. – Benno Nov 28 '14 at 01:10
  • Just like the solution I proposed this will have limited success. Once you start adding callable objects or use something like `std::bind` with similar function signatures, various placeholders and/or combination of argument values everything falls apart. – Captain Obvlious Nov 28 '14 at 02:03
  • Also, using `target_type()` isn't portable or going to work as designed once you add two distinct function pointers with the same signature. I did however find use for it in cases where the managed pointer is not a pointer to a free function (which the call to `target` will fail on. I used this to support lambdas and callable objects - `if (leftptr == nullptr || rightptr == nullptr) { return left.target_type().name() < right.target_type().name(); } ` – Captain Obvlious Nov 28 '14 at 02:10
  • I haven't tested it with anything complicated, but based on my understanding of the implementation of `std::function`, the second approach should work for all cases since it compares the actual byte pattern of the function target. (Concerning `target_type()`, you are of course completely right) – Benno Nov 28 '14 at 02:16
1

Would a std::vector<std::function<void(int)>> work?

If you want to order your functions it seems like you'd be the only one who knew the order anyway.

Perhaps creating an enum to index the std::vector would also be helpful for ordering?

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288