c++templatesc++17class-templateparameter-pack

Packing, unpacking and storing parameter pack in a tuple


I have a function pointer and parameters, and I'd like to save these and potentially modify them and call the function with them.

I've seen parts of this answered however I'm unsure how a complete solution would look like, I apologize, but I don't really understand parameter packs, and how they relate to tuple.

Here's bits of my code that I'm trying to make work together:

I have this which is basically just calling the function, I think this can be used to save the call into the "callable" structure, to make an interface.

from this question: C++ generic function call with varargs parameter

template<typename R, typename... Args>
auto call(R(*function)(Args...), Args... args) -> typename std::enable_if<!std::is_same<R, void>::value, R>::type 
{
    return function(args...);
}

template<typename... Args>
void call(void (*function)(Args...), Args... args)
{
    function(args...);
}

This structure should store the parameters and the function pointer. (thanks to RaymondChen for pointing out how it should be correctly)

template<typename R, typename... Args>
struct callable
{
    R(*function)(Args...);
    std::tuple<Args...> params;

    callable(Args... argv):
    params(std::make_tuple(argv...))
    {}

    // .....
};

I'm still unsure how to call back the function with the std::tuple. As far as I understand I should somehow turn the tuple back to Args... and then call it.

**What I'm trying to achieve:


Solution

  • If you have started storing the function and required parameters for it in the Callable, I would suggest just provide a operator() overload for calling the Callable with its stored parameters.

    Something along the line:

    // Functor to store the function and its parameters
    template<typename R, typename... Args>
    class Callable /* final */
    {
        R(*mFuncPtr)(Args...) { nullptr };  //  function pointer.
        std::tuple<Args...> mParams{};      //  store the parameters as std::tuple
    
    public:
        explicit constexpr Callable(R(*func)(Args...))
            : mFuncPtr{ func } {}
    
        explicit constexpr Callable(R(*func)(Args...), Args&&... argv)
            : mFuncPtr{ func }
            , mParams{ std::make_tuple(std::forward<Args>(argv)...) } {}
    
        // optional setter for mParams
        void set(Args&&... argv)
        {
            mParams = std::make_tuple(std::forward<Args>(argv)...);
        }
    
        auto operator()() -> std::conditional_t<std::is_void_v<R>, void*, R>
        {
            if constexpr (std::is_void_v<R>) 
            {  
                // invoke the function pointer with tuple pack. 
                std::apply(mFuncPtr, mParams);
                return nullptr;     // Use nullptr for void return type
            }
            else 
            {
                return std::apply(mFuncPtr, mParams);;
            }
        }
    
        // Print the stored parameters
        void printParams() const
        {
            std::cout << "(";
            printParamsImpl(std::index_sequence_for<Args...>{});
            std::cout << ")\n";
        }
    
    private:
        template<std::size_t... I>
        void printParamsImpl(std::index_sequence<I...>) const
        {
            (..., (std::cout << (I == 0 ? "" : ", ") << std::get<I>(mParams)));
        }
    };
    

    (See live example demo)