Tuple Forwarding

C++11 provides the very useful std::forward_as_tuple function. Unfortunately, there isn’t any standard way to unpack the tuple to the parameters of a function.

To do this, template recursion and forwarding can be used. Recursing over a tuple allows for the extraction of the arguments, at the end eval can evaluate the result:

template<size_t index>
struct tuple_eval_helper
{
    template<class Func,class TArgs,class... Args>
    static auto evaluate(Func&& f, TArgs&& t_args, Args&&... args)
        -> decltype(tuple_eval_helper<index-1>::evaluate(
            std::forward<Func>(f),
            std::forward<TArgs>(t_args),
            std::get<index>(std::forward<TArgs>(t_args)),
            std::forward<Args>(args)...
            ))
    {
        return tuple_eval_helper<index-1>::evaluate(
            std::forward<Func>(f),
            std::forward<TArgs>(t_args),
            std::get<index>(std::forward<TArgs>(t_args)),
            std::forward<Args>(args)...
            );
    }
};

template<>
struct tuple_eval_helper<0>
{
    template<class Func,class TArgs,class... Args>
    static auto evaluate(Func&& f, TArgs&& t_args, Args&&... args)
        -> decltype(eval(
            std::forward<Func>(f),
            std::get<0>(std::forward<TArgs>(t_args)),
            std::forward<Args>(args)...
            ))
    {
        return eval(
            std::forward<Func>(f),
            std::get<0>(std::forward<TArgs>(t_args)),
            std::forward<Args>(args)...
            );
    }
};

template<class Func,class TArgs>
auto tuple_eval(Func&& f, TArgs&& t_args)
    -> decltype(tuple_eval_helper<std::tuple_size<
            typename std::remove_reference<TArgs>::type>::value-1>::evaluate(
        std::forward<Func>(f),
        std::forward<TArgs>(t_args)
        ))
{
    return tuple_eval_helper<std::tuple_size<
            typename std::remove_reference<TArgs>::type>::value-1>::evaluate(
        std::forward<Func>(f),
        std::forward<TArgs>(t_args)
        );
}

Which you can use like this:

tuple_eval(std::plus<int>(),std::forward_as_tuple(1,2));

Now, suppose you have a tuple of functions that you want to call with the parameters? In similar fashion, recursively extract the functions, evaluate them and forward the result to the end where they are repackaged into another tuple:

template<size_t index>
struct multi_tuple_eval_helper
{
    template<class TFuncs,class TArgs,class... Results>
    static auto evaluate(TFuncs&& t_funcs, TArgs&& t_args, Results&&... results)
        -> decltype(multi_tuple_eval_helper<index-1>::evaluate(
            std::forward<TFuncs>(t_funcs),
            std::forward<TArgs>(t_args),
            tuple_eval(std::get<index>(t_funcs),t_args),
            std::forward<Results>(results)...
            ))
    {
        return multi_tuple_eval_helper<index-1>::evaluate(
            std::forward<TFuncs>(t_funcs),
            std::forward<TArgs>(t_args),
            tuple_eval(std::get<index>(t_funcs),t_args),
            std::forward<Results>(results)...
            );
    }
};

template<>
struct multi_tuple_eval_helper<0>
{
    template<class TFuncs,class TArgs,class... Results>
    static auto evaluate(TFuncs&& t_funcs, TArgs&& t_args, Results&&... results)
        -> decltype(std::tuple<decltype(tuple_eval(std::get<0>(t_funcs),t_args)),Results...>(
            tuple_eval(std::get<0>(t_funcs),t_args),
            std::forward<Results>(results)...
            ))
    {
        return std::tuple<decltype(tuple_eval(std::get<0>(t_funcs),t_args)),Results...>(
            tuple_eval(std::get<0>(t_funcs),t_args),
            std::forward<Results>(results)...
            );
    }
};

template<class TFuncs,class TArgs>
auto multi_tuple_eval(TFuncs&& t_funcs, TArgs&& t_args)
    -> decltype(multi_tuple_eval_helper<std::tuple_size<
            typename std::remove_reference<TFuncs>::type>::value-1>::evaluate(
        std::forward<TFuncs>(t_funcs),
        std::forward<TArgs>(t_args)
        ))
{
    return multi_tuple_eval_helper<std::tuple_size<
            typename std::remove_reference<TFuncs>::type>::value-1>::evaluate(
        std::forward<TFuncs>(t_funcs),
        std::forward<TArgs>(t_args)
        );
}

Which can be used like this:

multi_tuple_eval(
    std::forward_as_tuple(std::plus<int>(),std::multiplies<int>()),
    std::forward_as_tuple(3,4)
    );

The result is the tuple (7,12).

Esoteric and abusive to the compiler, that’s what we’re about here at Functional C++!

Note: Be careful with move semantics and invalidating stuff when using this.

Edit: Changed multi_tuple_eval to use the tuple constructor. It will now correctly return references.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s