Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling a function with specific arguments from a variadic templated class

template< typename ... Args >
class Message {
public:
    Message( Args&& ... args ) {
        mArgs = std::make_tuple( args ...  );
    }

    std::tuple< Args ... > mArgs;
    typedef std::function< void ( Args ... ) > HandlerType;

    void Consume( HandlerType handler ) {
        // handler( mArgs ); 
        // How does one unpack this?
    }
};

// Testing code
Message<int, int> msg(1, 2);

msg.Consume( [] ( int i, int j ) {
    std::cout << i << ',' << j << '\n';
});

I'm attempting a simple message passing API, trying to provide a simple templated interface for messages and arguments. I'm running into an issue when I want to pass the arguments into a function.

I've not used variadic templates too much, and was wondering if there is an elegant solution to my problem.

like image 986
DubyaDubyaDubyaDot Avatar asked Dec 15 '22 10:12

DubyaDubyaDubyaDot


2 Answers

Here is a simple framework for unpacking a tuple and providing its elements as arguments to a given function:

namespace detail
{
    template<int... Is>
    struct seq { };

    template<int N, int... Is>
    struct gen_seq : gen_seq<N - 1, N - 1, Is...> { };

    template<int... Is>
    struct gen_seq<0, Is...> : seq<Is...> { };

    template<typename F, typename... Ts, int... Is>
    void call_with_tuple(F&& f, std::tuple<Ts...> const& t, seq<Is...>)
    {
        (std::forward<F>(f))(std::get<Is>(t)...);
    }
}

template<typename F, typename... Ts>
void call_with_tuple(F&& f, std::tuple<Ts...> const& t)
{
    detail::call_with_tuple(std::forward<F>(f), t, 
        detail::gen_seq<sizeof...(Ts)>());
}

Inside your Consume() function you could then just do:

call_with_tuple(handler, mArgs);

With some more context:

#include <iostream>
#include <functional>

template< typename ... Args >
class Message {
public:
    Message( Args&& ... args ) {
        mArgs = std::make_tuple( args ...  );
    }

    std::tuple< Args ... > mArgs;
    typedef std::function< void ( Args ... ) > HandlerType;

    void Consume( HandlerType handler ) {
        call_with_tuple(handler, mArgs);
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    }
};

And here is a live example.

like image 76
Andy Prowl Avatar answered Jan 19 '23 00:01

Andy Prowl


How about this?

template<size_t... indexes>
struct index_tuple {};

template<size_t head, size_t... indexes>
struct index_tuple<head, indexes...> {
    typedef typename index_tuple<head-1, head-1, indexes...>::type type;
};

template<size_t... indexes>
struct index_tuple<0, indexes...> {
    typedef index_tuple<indexes...> type;
};

template<typename... Args>
struct index_tuple_maker {
    typedef typename index_tuple<sizeof...(Args)>::type type;
};


template< typename ... Args >
class Message {
public:
    Message( Args&& ... args ) {
        mArgs = std::make_tuple( args ...  );
    }

    std::tuple< Args ... > mArgs;
    typedef std::function< void ( Args ... ) > HandlerType;

    void Consume( const HandlerType &handler ) {
        Consume(handler, typename index_tuple_maker<Args...>::type());
    }
private:
    template<size_t... ns>
    void Consume(const HandlerType &handler, index_tuple<ns...>) {
        handler(std::get<ns>(mArgs)...);
    }
};

Live demo here.

like image 43
mfontanini Avatar answered Jan 19 '23 00:01

mfontanini