High performance solution for taking N arguments and returning N values

What can I use to make a function take N number of arguments, where N doesn't know at programming time, but is fixed at compile time (it's actually a template parameter)?

This function is an accessor function that is in a performance-critical way, so I am looking for the least overhead.

What comes to mind at first is this std::initializer_list

and, although cheap to me, it still remains an unnecessary object to create and copy. What's more, it has a fun way of accessing its elements with initializer_list::begin[i]

(which is another object I don't need) and doesn't limit the number of arguments to N exactly, but that's a minor complaint.

Second, there are template parameter packages. Could they be a viable candidate? I have to use recursion to access the N values.

What I was aiming for I was trying to show in this pseudocode:

template<int dim, class T> class linear_searchspace {

    template<T...args> operator() (args) {
        return std::tie(some_kinda_structure[args[0]], some_kinda_structure[args[1]], some_kinda_structure[args[3]]);
    }

};

      

Is it possible to make it recursively that would actually work?

CONFIRMATION: args

must be coordinates. Each coordinate is an index of the value in the dimension. So N coordinates will be traversed, N values ​​will be returned. It's like accessing N vectors at the same time. I would like to add an offset to each argument that depends on the index of the argument, since it stores an array with the offset index corresponding to the index of the argument. The calculation will be simple arithmetic.

And what would be the appropriate return type? The structure it will access will most likely contain numeric values, no more than tuples. Is this the std::tuple

best thing I can do, or can you create something more perfect?

As far as arguments go, everything goes, even macros. I'd love to hear what tricks you have come up with over the years.

+3


source to share


3 answers


double data[]={1,2,3,4,5,6,7,8};
double& toy_map(int x){return data[x];}

template<class...Ts>
auto example(Ts&&...ts)
-> decltype(std::tie(toy_map(std::forward<Ts>(ts))...))
{
  static_assert(sizeof...(Ts)==5, "wrong parameter count");
  return std::tie(toy_map(std::forward<Ts>(ts))...);
}

      

Note that the order that is called toy_map

is not specified.

In C ++ 14, remove the line decltype

if you don't need SFINAE.



Replace 5

with N

in your real code.

If you want perfect overloading, you need to do SFINAE checks N

, but this is usually overkill.

+2


source


template <std::size_t DIM, typename T>
class linear_searchspace
{
public:
    template <typename... Args>
    inline constexpr auto operator()(Args... args) const
        noexcept(noexcept(T{std::declval<T>()}))
        -> std::tuple<decltype((void)args, T{std::declval<T>()})...>
    {
        static_assert(sizeof...(Args) == DIM, "wrong number of indices!");
        using Tuple = std::tuple<decltype((void)args, T{std::declval<T>()})...>;
        return get<Tuple>(make_index_sequence<sizeof...(Args)>{}, args...);
    }

private:
    template <typename Tuple, typename... Args, std::size_t... Is>
    inline constexpr Tuple get(index_sequence<Is...>, Args... args) const
        noexcept(noexcept(T{std::declval<T>()}))
    {
        return Tuple((some_kinda_structure[args] + Is)...);
        //                                       ^^^^
        // some calculation for each element based on index (here simple addition)
    }

    T some_kinda_structure[DIM]; // implementation defined
};

      

(implementation index_sequence

is in demo)

DEMO



The above solution achieves the best performance with objects constexpr

since the entire operation is evaluated at compile time:

int main()
{
    constexpr linear_searchspace<5, int> lsp{};

    // evaluated at compile-time
    constexpr auto t = lsp(0, 1, 2, 3, 4);
    static_assert(std::get<1>(t) == 1, "!");

    // evaluated at run-time
    auto t2 = lsp(4, 3, 2, 1, 0);
    assert(std::get<3>(t2) == 3);
}

      

+4


source


If your function parameters are the same, you can pass them over a vector. Put all the data in a vector, then pass the vector to a function.

class Coordinate;

std::vector<Coordinate> my_function(const std::vector<Coordinate>& data)
{
  const unsigned int items_in_data = data.size();
  //...
  return data;
}

      

A vector is dynamic and it can tell you how many elements are inside it.

The rule of thumb is that when a function requires many arguments, put the arguments in a structure or container and pass the structure or container.

+1


source







All Articles