c++functionreferencevariadic-templates

Passing references to function wrapper doesn't work correctly


I have the functionWrapper class, which should encapsulate function. It works fine but arguments are unable to being passed by reference.

Here is the code:

#include <iostream>
#include <functional>
#include <tuple>
#include <type_traits>

template <typename T>
struct function_traits;

// Specialization for function pointers  
template <typename Ret, typename... Args>
struct function_traits<Ret(*)(Args...)> {
    using return_type = Ret;
    using args_type = std::tuple<Args...>;
};

// Specialization for std::function  
template <typename Ret, typename... Args>
struct function_traits<std::function<Ret(Args...)>> {
    using return_type = Ret;
    using args_type = std::tuple<Args...>;
};

// Specialization for lambdas and functors  
template <typename T>
struct function_traits : function_traits<decltype(&T::operator())> {};
 


class functionWrapper {
    template <typename... Args>
    class B;

    class A {
    public:
        template <typename F>
        static auto castToB(F func) {
            using ArgsTuple = typename function_traits<F>::args_type;

            return castToBImpl(std::make_index_sequence<std::tuple_size<ArgsTuple>::value>{}, func);
        }
        virtual ~A(){}
    private:
        template <std::size_t... Is, typename F>
        static auto castToBImpl(std::index_sequence<Is...>, F func) {
            // Create B with the correct argument types based on the number of arguments  
            return new B<typename std::tuple_element<Is, typename function_traits<F>::args_type>::type...>(func);
        }
    };
 
    template <typename... Args>
    class B : public A {
    public:
        std::function<void(Args...)> function;

        template <typename F>
        B(F f) : function(f) {}
        virtual ~B(){}
    };
private:
    A* ptr = nullptr;
public:
    template <typename F>
    functionWrapper(F f) {
        ptr = static_cast<A*>(A::castToB(f));
    }
    functionWrapper(){}
    virtual ~functionWrapper(){
        delete ptr;
    }
    template <typename... Args>
    void operator()(Args... args) {
        if (ptr)
        static_cast<B<Args...>*>(ptr)->function(std::forward<Args...>(args...)); 
    }

};

Using:

void foo(int& a){
    std::cout << a++ << '\n';
}

int main() {
    int a = 5;
    functionWrapper wrap{foo};
    wrap(a);
    std::cout << a << '\n';
    return 0;
}

Output is:

5
5

Instead of expected:

5
6

Solution

  • Changed this:

    template <typename... Args>
    void operator()(Args... args) {
            if (ptr)
            static_cast<B<Args...>*>(ptr)->function(std::forward<Args...>(args...)); 
    }
    

    To this:

    template <typename... Args>
    void operator()(Args&&... args) {
        if (ptr)
        static_cast<B<Args...>*>(ptr)->function(std::forward<Args>(args)...);
    }
        
    

    Looks like its working correctly