c++ vector of function with template arguments

Viewed 461
#include<iostream>
#include<functional>
#include <unordered_set>
#include <vector>
#include <unordered_map>
#include <variant>




class Button
{
public:
    Button(int _val):val(_val){}
    ~Button();
    int val;
    
    template<typename F, typename...InArgs>
    void addListener(std::function<F(InArgs...)>(f), InArgs... args) {
        listener.push_back(f(args...));
    }

    template<typename F, typename...InArgs>
    void callMethod(std::function<F(InArgs...)>(f), InArgs... args) {
        f(args...);
    }
    
    void print() { std::cout << "Button value -> " << val << std::endl; }
private:
    template<typename F,typename...InArgs>
    static std::vector<std::function<F(InArgs...)>> listener;
    
};

inline void test1() {
    std::cout << "Printing test1 method...\n";
}
inline void test2(Button* b) {
    std::cout << "Button value is 20.\n";
    b->val = 20;
}
inline void test3(Button* b, int a) {
    std::cout << "Button value is passed parameter.\n";
    b->val = a;
}
inline void test4(int a) {
    std::cout << "print " << a << std::endl;
}
class AppThree
{
public:
    static void run() {
        Button* button = new Button(5);
        button->print();
        //button->callMethod(std::function<void(int)>(&test4), 1); //work it
        button->addListener(std::function<void(int)>(&test4),1); // error
        

    }
};

I want push functions with arguments to vector. I want calling all of functions from vector but i can't adding functions. I'm getting C3245 error . I don't know where failing. If my idea incorrect please show true way. Sorry my bad english :D

1 Answers

You have a member variable template listener, which means that you have one vector for each function type. The compiler can't infer which one you mean.
You're also trying to push_back the result of calling the function, rather than the function itself, and you're not storing the arguments.

You can't do this the way you planned, because listener isn't one variable with elements of different types – it's (potentially) many variables, each with a different type.
In order to call them, you would need to enumerate the variable instantiations, and that's not possible.
Also, you would need to figure out some way to store the arguments.

Since you want to pass the arguments later, you only need one type, and you can capture them in a lambda:

class Button
{
public:
    Button(int _val):val(_val){}
    ~Button();
    int val;
    
    template<typename F, typename...InArgs>
    void addListener(std::function<F(InArgs...)> f, InArgs... args) {
        listener.push_back([=]() { f(args...); });
    }
    void action()
    {
        for (auto& f: listener)
            f();
    }
    void print() { std::cout << "Button value -> " << val << std::endl; }
    
private:
    static std::vector<std::function<void()>> listener;
    
};

std::vector<std::function<void()>> Button::listener;

void test3(Button* b) {
    
    std::cout << "listened:";
    b->print();
}

void test4(int a) {
    
    std::cout << "print " << a << std::endl;
}

int main()
{
    Button* button = new Button(5);
    button->print();
    button->addListener(std::function<decltype(test4)>(&test4), 1);
    button->addListener(std::function<decltype(test3)>(&test3), button);
    button->action();
}

You could also leave the listener construction entirely to the user:

class Button
{
public:
    Button(int _val):val(_val){}
    ~Button();
    int val;
    
    void addListener(std::function<void()> f) {
        listener.push_back(f);
    }
    void action()
    {
        for (auto& f: listener)
            f();
    }
    void print() { std::cout << "Button value -> " << val << std::endl; }
    
private:
    static std::vector<std::function<void()>> listener;
    
};

std::vector<std::function<void()>> Button::listener;

void test3(Button* b) {
    
    std::cout << "listened:";
    b->print();
}

void test4(int a) {
    
    std::cout << "print " << a << std::endl;
}

int main()
{
    Button* button = new Button(5);
    button->print();
    button->addListener([]() { test4(1); });
    button->addListener([button]() { test3(button); });
    button->action();
}
Related