Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call a function with variadic arguments using parameter pack expansion, but modify th i-th parameter

Suppose we have a function

template<typename T, typename... Args>
T f(Args... args);

and we want to call f in an other function

template<typename... Args>
void bar(Args... args) {
    // f(args1, ..., args(i-1), "modified argsi", args(i+1), ..., argsn);
}

and modify the i-th argument (and leave the other arguments untouched) for all i = 1, ..., n. f will return a result and I want to store all n results in a valarray.

How can we do that?

like image 858
0xbadf00d Avatar asked Sep 28 '22 22:09

0xbadf00d


2 Answers

With I a compile time value, you may do:

namespace detail
{

    template<std::size_t I>
    struct impl
    {
        template <typename TUPLE>
        auto operator() (TUPLE&& t) { return std::get<I>(t); }
    };

    template<>
    struct impl<2>
    {
        template <typename TUPLE>
        auto operator() (TUPLE&& t) { return std::get<2>(t) + 40; }
    };

    template <std::size_t... Is, typename TUPLE>
    void bar(std::index_sequence<Is...>, TUPLE&& Tuple) {
        return f(impl<Is>{}(std::forward<TUPLE>(Tuple))...);
    }

}

template<typename... Args>
void bar(Args... args) {
    detail::bar(std::index_sequence_for<Args...>(), std::forward_as_tuple(args...));
}

Live Demo

With i a runtime value:

namespace detail
{

    template<class T>
    auto modifier(T&& arg, std::size_t i, std::size_t pos) {
        if (i == pos)
            return arg + 40; // Or appropriate modification.
        else
            return std::forward<T>(arg);
    }

    template <std::size_t... Is, typename... Args>
    void bar(std::size_t pos, std::index_sequence<Is...>, Args&&... args) {
        return f(modifier(std::forward<Args>(args), Is, pos)...);
    }

}

template<typename... Args>
void bar(std::size_t pos, Args&&... args) {
    detail::bar(pos, std::index_sequence_for<Args...>(), std::forward<Args>(args)...);
}

Live Demo

like image 155
Jarod42 Avatar answered Oct 06 '22 19:10

Jarod42


Well, a lookup table does the job, if branching is to be avoided. Rough sketch:

#include <tuple>
#include <utility>
#include <iostream>

void foo(std::string a, std::string b, std::string c) {
    std::cout << a << '|' << b << '|' << c;
}

template <typename, typename> struct passerAux;
template <std::size_t... prevI, std::size_t... followI>
struct passerAux<std::index_sequence<prevI...>, std::index_sequence<followI...>> {
    template <typename... Args>
    static decltype(auto) passer( Args&&... args ) {
        auto tuple = std::forward_as_tuple(std::forward<Args>(args)...);
        return foo( std::forward<std::tuple_element_t<prevI, decltype(tuple)>>(std::get<prevI>(tuple))...,
                    "ModifiedArg",
                    std::forward<std::tuple_element_t<followI+sizeof...(prevI)+1, decltype(tuple)>>(std::get<followI+sizeof...(prevI)+1>(tuple))... );
    }
};

template <typename... Args, std::size_t... indices>
decltype(auto) passer( std::size_t i, std::index_sequence<indices...>, Args&&... args ) {
    void(*lookup[])(Args&&...) {
        passerAux<std::make_index_sequence<indices>, std::make_index_sequence<sizeof...(Args)-indices-1>>::passer...
    };
    return lookup[i](std::forward<Args>(args)...);
}

template <typename... Args>
decltype(auto) passer( std::size_t i, Args&&... args ) {
    return passer(i, std::make_index_sequence<sizeof...(Args)>{}, std::forward<Args>(args)... );
}

int main() {
    passer(0, "A", "B", "C"); std::cout << '\n';
    passer(1, "A", "B", "C"); std::cout << '\n';
    passer(2, "A", "B", "C");
}

Demo.

like image 30
Columbo Avatar answered Oct 06 '22 19:10

Columbo