1

after saw Unity's delegates and events, i'm trying to code my own: I want to create a class with a variadic template, to specify the return type of the functions, and optionals arguments.

template <typename Ret, typename... Args>
class MyDelegate{
    std::vector<Ret(*)(Args...)> vec;

    public:
    void operator+=( const Ret(*)(Args...)& newElement)
    {
        vec.push_back(newElement);
    }


    Ret operator()(const Args&... args)
    {
        for (auto i = vec.begin(); i != vec.end(); ++i)
            (*i)(args...);
    }
};

As you can see, i would like this class to be used this way:

MyDelegate<void> voidDelegate;
MyDelegate<void, int> intDelegate;
MyDelegate<int, char, boolt> miscDelegate;

and "adding" functions to each one using += operators, like:

voidDelegate += voidFunc;
//etc...

I'm having problem with the += operator for now, because VS don't accept this:

MyDelegate<void, int> delegate1;
delegate1 += [](const int a)->void{std::cout << a << std::endl; };

The lambda function is correct: it take an int and return void, so i don't understand whats' wrong.

fudo
  • 963
  • 7
  • 18
  • In a closely related topic (delayed dispatching), you may find [**this question and top-answer**](http://stackoverflow.com/a/7858971/1322972) particularly interesting. It hass come in handy for me numerous times. – WhozCraig Jul 10 '15 at 15:17

3 Answers3

3

The issue is that your std::vector stores function pointers. It doesn't store std::bind objects, it doesn't store member functions, it doesn't store functors and it doesn't store lambdas. You are trying to add a lambda to it, hence the failure.

If you want to store any kind of object which supports calling with the correct argument and return types, you want std::function:

using FunType = std::function<Ret(Args...)>;
std::vector<FunType> vec;

Demo

Incidentally, you could improve your solution by perfect-forwarding your operator() args and copying your newElement arg in the interface and moving it into the std::vector.

TartanLlama
  • 59,364
  • 11
  • 141
  • 183
  • If you have a std::vector of std::function, it can be difficult to remove an individual std::function at a later time. Normally if you wanted to remove something from a vector, you'd find it first to get an iterator and then pass the iterator into vector::erase. In the case of std::function, the find won't work because std::function's operator == is... strange (I can't remember the details). Instead, you might want to use something like a vector of pair where the unsigned is some sort of serial number. Then you can find using that serial number at a later time. – peterpi Jul 10 '15 at 15:46
  • ... the only reason I'm so familiar with the problem is that I've spent a few days doing exactly this at my day job :) – peterpi Jul 10 '15 at 15:48
1

Your delegate accepts only function pointers. A lambda is not a function pointer. However, the lambda that you are trying to doesn't capture anything. Which means that it can be converted to a function pointer thanks to some sorcery:

MyDelegate<void, int> delegate1;
delegate1 += +[](const int a)->void{std::cout << a << std::endl; };
             ↑↑↑

However, once you want to allow functors that have member variables, the extra + won't work:

delegate1 += +[x](const int a) { ... }; // error: no match for operator+

At which point you'll definitely have to use TartanLlama's suggestions of std::function.

Community
  • 1
  • 1
Barry
  • 247,587
  • 26
  • 487
  • 819
  • 1
    @TartanLlama Super useful for using with C libraries that require function pointers or Boost.Python, which you'd think would allow functors, but doesn't. – Barry Jul 10 '15 at 18:53
0

@TartanLlama's right, std::function is what you need.

And the calling loop can be folded to for (auto handler : vec) handler(args...);