Home > Net >  std::function & std::forward with variadic templates
std::function & std::forward with variadic templates

Time:11-29

Recently I was reading about variadic templates and based on an example I've seen online I was trying to implement a basic event-system. So far it seems to work fine but I was trying to go a step further and allow N number of arguments to be passed to an event handler function / callback, unfortunately the build error I'm getting is the following and I'm not sure what I'm doing wrong. I looked into similar source codes but still cant figure out what's the issue.

D:\Development\lab\c-cpp\EventEmitter3\src\main.cpp:30:68: error: parameter packs not expanded with '...':
         return std::any_cast<std::function<R(Args)>>(eventCallback)(std::forward<Args>(args)...);
                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

D:\Development\lab\c-cpp\EventEmitter3\src\main.cpp:30:68: note:         'Args'

Build finished with error(s).

Here is what I have so far, if you remove the ... the event system works fine for the 2 registered events in main.

#include <any>
#include <string>
#include <iostream>
#include <functional>
#include <unordered_map>

class EventEmitter
{

private:
    std::unordered_map<std::string, std::any> events;

public:
    EventEmitter() {}

    void on(const std::string &eventName, const std::any &eventCallback)
    {
        events[eventName] = eventCallback;
    }

    template <typename R>
    R emit(const std::string &eventName)
    {
        const std::any &eventCallback = events[eventName];
        return std::any_cast<std::function<R(void)>>(eventCallback)();
    }

    template <typename R, typename... Args>
    R emit(const std::string &eventName, Args &&...args)
    {
        const std::any &eventCallback = events[eventName];
        return std::any_cast<std::function<R(Args)>>(eventCallback)(std::forward<Args>(args)...);
    }

    virtual ~EventEmitter() {}
};

int fun1()
{
    std::cout << "fun1" << std::endl;
    return 1;
}

double fun2(int i)
{
    std::cout << "fun2" << std::endl;
    return double(i);
}

double fun3(int x, int y)
{
    std::cout << "fun3" << std::endl;
    return double(x   y);
}

int main(int argc, char *argv[])
{
    EventEmitter e;

    e.on("fun1", std::function<int(void)>(fun1));
    e.on("fun2", std::function<double(int)>(fun2));
    

    e.emit<int>("fun1");
    e.emit<double, int>("fun2", 1);
    
    // Variadic would have been handy right here I guess?
    // e.on("fun3", std::function<double(int, int)>(fun3)); 
    // e.emit<double, int>("fun3", 1, 2); 

    return 0;
}

How can I fix this?

CodePudding user response:

Well, you need to expand it.

return std::any_cast<std::function<R(Args...)>>(eventCallback)(std::forward<Args>(args)...);
                                     ^^^^^^^
  • Related