super
super

Reputation: 12928

Generating lambda from class template

I have a class that registers callback functions and calls them later that looks like this.

template<typename ReturnType, typename... Args>
class Signal {
    std::vector<std::function<ReturnType(Args...)>> function;
public:
    template<typename... Args2>
    ReturnType operator()(Args2&&... args2) {
        ReturnType ret;
        for (auto& func : function)
            ret = func(std::forward<Args2>(args2)...);
        return ret;
    }

    template<typename Func>
    void func(Func const &func) {
        function.push_back(std::function<ReturnType(Args...)>(func));
    }

    template<typename Class, typename Instance>
    void mfunc(ReturnType(Class::*func)(Args...), Instance &instance) {
        mfunc2(func, instance, make_int_sequence<sizeof...(Args)>{});
    }
    template<typename Class, typename Instance, int... I>
    void mfunc2(ReturnType(Class::*func)(Args...), Instance &instance, int_sequence<I...>) {
        using namespace std::placeholders;
        function.push_back(std::function<ReturnType(Args...)>(std::bind(func, &instance, placeholder_template<I>{}...)));
    }
};

#include <iostream>

class foo {
public:
    int bar(int x, double y) {
        std::cout << x << " and " << y << std::endl;
        return x*2;
    }
};

int main() {
    foo foo1;
    Signal<int, int, double> sig;
    sig.mfunc(&foo::bar, foo1);
    std::cout << "Return: " << sig(5,5.5) << std::endl;
}

I heard a talk from Stephan T. Lavavej today, and one of the things he was saying is that std::bind should be avoided and use lambdas instead. So to learn something new I figured I would try and change the std::bind call in mfunc2 to a lambda, but I'm quite new to templates and can't figure out how to generate the code I want.

The current placeholder_template with make_int_sequence I found here on SO, but I can't really wrap my head around how exactly it works, or where to find any good reading on it...

Args... holds the argument types that should be accepted by the lambda, but I need to somehow create variable names such as var1, var2, var3 ect depending on the sizeof...(Args) and then merge them together.

So for example < int, int, int >, Args... would hold int, int. I then want to construct the lambda as

[func, &instance](int var1, int var2) -> ReturnType { return func(&instance, var1, var2); }

How could I accomplish this?

Upvotes: 2

Views: 127

Answers (1)

Jodocus
Jodocus

Reputation: 7581

This should do the job:

template<typename ReturnType, typename... Args>
class Signal {
    std::vector<std::function<ReturnType(Args...)>> function;
public:
    template<typename... Args2>
    ReturnType operator()(Args2&&... args2) {
        ReturnType ret;
        for (auto& func : function)
            ret = func(std::forward<Args2>(args2)...);
        return ret;
    }

    template<typename Func>
    void func(Func const &func) {
        function.push_back(std::function<ReturnType(Args...)>(func));
    }

    template<typename Class, typename Instance>
    void mfunc(ReturnType(Class::*func)(Args...), Instance& instance) {
        function.push_back([&instance, func](Args&&... args) { 
            return (instance.*func)(std::forward<Args>(args)...); 
        });
    }

};

https://ideone.com/gjPdWN

Note that in your operator(), you basically throw away all return values except the last one. Is that behaviour intended?

Upvotes: 5

Related Questions