I want to efficiently multiply the arguments from a parameter pack with the elements of a std::array:
int index(auto... Is, std::array<int,sizeof...(Is)> strides)
{
// pseudo-code
// int idx = 0;
// for(int i = 0; i < sizeof...(Is); ++i)
// idx += Is[i] * strides[i];
// return idx;
}
I can't quite wrap my brain around this one. I started down the road of an index sequence, but I could figure out how to incorporate the summation.
I am using c++17, so fold expressions are fair game if they would simplify the code.
Thanks for any pointers.
EDIT: Clarified the pseudo-code. The only pseudo part is the expression Is[i]
which refers to the i'th parameter pack argument.
T.C.'s answer below was perfect and here is my final code which is a member function:
unsigned int index(auto... indexes)
{
unsigned int idx = 0, i = 0;
(..., (idx += indexes * m_strides[i++]));
return idx;
}
As of this writing, the code compiles using gcc 6.3.0 with the -fconcepts flag, which brings in the Concept TS.
Using auto... indexes
is shorthand for template<typename Args> f(Args... indexes)
. I tried to use an unsigned int concept for the arguments, but I couldn't get that to work.
The (...,) fold is the key element and expands to something like (if you could actually [] into the parameter pack):
idx += indexes[0] * m_strides[i++], idx += indexes[1] * m_strides[i++], etc.
That was the insight I was missing.
I can't get auto...
to work, so I changed the signature of index
.
You will need an auxiliary function (index_helper
here) to use index_sequence
, since it relies on template argument deduction to fill in the indices.
#include <array>
#include <cstdio>
template <typename... T, size_t... i>
// ^~~~~~~~~~~
// use deduction to make {i...} = {0, 1, 2, ..., n}
static int index_helper(const std::array<int, sizeof...(T)>& strides,
std::index_sequence<i...>,
T... Is)
{
return (0 + ... + (strides[i] * Is));
}
template <typename... T>
int index(const std::array<int, sizeof...(T)>& strides, T... Is) {
return index_helper(strides, std::make_index_sequence<sizeof...(T)>(), Is...);
// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// generates {0, 1, 2, ..., n}
}
int main() {
printf("%d\n", index({1, 100, 100000, 1000}, 2, 3, 5, 7));
// 507302
}
If you can hammer down the argument pack into one single type that is cheap to copy/move, you can just make it into an array:
T arr[] = { static_cast<T>(Is)... }; // for some T, possibly common_type_t<decltype(Is)...>
Then you can just turn your pseudocode into real code.
If that's not feasible, a comma fold can be used:
int idx = 0, i = 0;
(..., (idx += Is * strides[i++]));
return idx;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With