c++17 efficiently multiply parameter pack arguments with std::array elements - c++

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;

Related

C++ constexpr function to checksum an array

Is there away to achieve the behavior shown in the example (non compiling) code below? I (think I) understand why it doesn't compile given that the required calls to std::initializer_list functions are not constexpr.
The goal is to be able to create an array from a constant initialiser list when an additional element is appended to the end that is the sum of the preceeding elements.
All the posts I found on initialising arrays at compile time required lots of complex recursive template function calls and all were related to generating sequences of numbers.
#include <initializer_list>
#include <array>
template <typename T> constexpr auto CheckSummedArray(const std::initializer_list<T> & i)
{
std::array<T, i.size() + 1> res;
std::copy(i.begin(), i.end(), res.begin());
auto cs = T();
for (auto r : i)
{
cs += r;
}
res[res.size() - 1] = cs;
return res;
}
constexpr auto testArray = CheckSummedArray<int>({1,2,3,4});
static_assert(testArray.size() == 5);
static_assert(testArray[0] == 1);
static_assert(testArray[4] == 9);
The issue is not the calls to the members of std::initializer_list, those functions are actually constexpr. The issue is that you are using the result of i.size() in a template parameter, but i is not a constant expression, since it's a function parameter.
You can solve this by making the argument an array type, and so you can deduce its size with a template parameter:
template <typename T, std::size_t N>
constexpr auto CheckSummedArray(T const(&i)[N])
{
std::array<T, N + 1> res{};
std::copy(i, i + N, res.begin());
res.back() = std::accumulate(i, i + N, 0);
return res;
}
Here's a demo.
I cleaned up the function a little by using an algorithm instead of a loop. If you're not using C++20, you don't have access to constexpr algorithms, so your copy and accumulate will need to be raw loops anyway.
for(std::size_t j = 0; j < N; ++j)
{
res[j] = i[j];
res.back() += i[j];
}
Here's a C++17 demo.

`static_assert` on `std::initializer_list<T>::size` in a function

In my unit tests, I want a quick and (cleanish) dirty way to assign values to a static-size C-array from an initializer_list. I'm not a complete beast, so I want to static_assert that the sizes are the same. I wrote a helper function set_array to do this:
template <typename T, std::size_t N>
constexpr void set_array(T (&x)[N], std::initializer_list<T>&& list) {
assert(list.size() == N); // why can't I static_assert in C++17?
for (std::size_t i = 0; i < N; ++i)
x[i] = list.begin()[i];
}
with the intention of using it as set_array(foo, {1, 2, 3, 4}); with foo declared like int foo[4].
I'm using C++17, so std std::initializer_list<T>::size is constexpr, but as soon as I pass the list through a function call, I lose the privilege of treating it as constexpr since I can't constrain the function parameters to be constexpr.
This feels like it should have a simple solution that I'm not seeing. Sure, I could imagine some perverse metaprogramming games I could play to encode the size in a type, but this is a simple little helper that's supposed to make things clean and readable, and I don't want to go nuts.
Question: Is there a simple solution, or should I just live with the runtime assert? (Yes, I know that if I'm given a simple solution, I'm going to feel stupid for not having seen it myself.) Think I'm going about it the wrong way? That's fine, I'm open to suggestions and appreciate the criticism.
Details:
For completeness, here is the compiler error and version info. In Clang version 8.0.0-3 (That comes with Ubuntu clang-8) I get:
error: static_assert expression is not an
integral constant expression
static_assert(list.size() == N);
^~~~~~~~~~~~~~~~
And with GCC 8.3.0 I get a similar error, additionally telling me that list is not a constant expression.
The reason this fails even though size is constexpr is because list is not a constexpr variable so any member function calls on it will also not be constexpr.
All is not lost though. What you can do is use a std::array instead of a std::initializer_list which lets you even get rid of the static_assert like:
template <typename T, std::size_t N>
constexpr void set_array(T (&x)[N], std::array<T, N>&& list) {
for (std::size_t i = 0; i < N; ++i)
x[i] = list[i];
}
int main()
{
int arr[4];
set_array(arr, {1,2,3,4});
std::cout << arr[3];
}
If you tried using
set_array(arr, {1,2,3,4,5});
then you would get a compiler error like
main.cpp:12:16: note: candidate function [with T = int, N = 4] not viable: cannot convert initializer list argument to 'std::array<int, 4UL>'
constexpr void set_array(T (&x)[N], std::array<T, N>&& list) {
Arguments to functions are not constants.
This is legal in C++:
void foo( bool which ) {
std::initializer_list<int> a = {1,2,3};
std::initializer_list<int> b = {1,2,3,4};
int x[4];
std::initializer_list<int> c = which?a:b;
set_array(x, c);
}
try this:
template<class T>
struct block_deduction_t{using type=T;};
template<class T>
using block_deduction = typename block_deduction_t<T>::type;
template <typename T, std::size_t N>
constexpr void set_array(T (&x)[N], block_deduction<T const(&)[N]> list) {
for (std::size_t i = 0; i < N; ++i)
x[i] = list[i];
}
now this permits you to omit trailing elements, but they will be zero-initialized.
Live example.
Then again, this solution:
template <typename T, std::size_t N>
constexpr void set_array(T (&x)[N], T const(&list)[N]) {
for (std::size_t i = 0; i < N; ++i)
x[i] = list[i];
}
actually blocks under-sized right hand sides. So might be better.
Syntax exactly matches yours.
If you want a pretty error message, and for the right hand types to be converted to match the left hand side types:
template <typename T, std::size_t N, std::size_t M>
constexpr void set_array(T (&x)[N], block_deduction<T> const(&list)[M]) {
static_assert(M==N, "wrong number of elements");
for (std::size_t i = 0; i < N; ++i)
x[i] = list[i];
}
there are many variants.

C++14: How to group variadic inputs by template parameter?

Say I have two classes:
template <unsigned N>
class Pixel {
float color[N];
public:
Pixel(const std::initializer_list<float> &il)
{
// Assume this code can create a Pixel object from exactly N floats, and would throw a compiler error otherwise
}
};
template <unsigned N>
class PixelContainer {
std::vector<Pixel<N>> container;
};
What I'm trying to do is to write a constructor for PixelContainer such that:
It would instantiate correctly for the following cases (example, not exhaustive):
PixelContainer<3> pc1(1, 2, 3) // Creates a container containing one Pixel<3> objects
PixelContainer<3> pc2(1, 2, 3, 4, 5, 6) // Creates a container containing 2 Pixel<3> objects
PixelContainer<2> pc3(1, 2, 3, 4, 5, 6) // Creates a container containing 3 Pixel<2> objects
It would not compile for the following cases (as example, not exhaustive):
PixelContainer<3> pc4(2, 3) // Not enough arguments
PixelContainer<2> pc5(1, 2, 3, 4, 5) // Too many arguments
How do I achieve the above using template meta-programming? I feel it should be achievable, but can't figure out how. Specifically, I do not want to be doing the grouping myself e.g
PixelContainer<2> pc2({1, 2}, {3, 4}, {5, 6}) // Creates a container containing 3 Pixel<2> objects
(See this question for the inspiration behind mine)
template<class T, std::size_t I, std::size_t...Offs, class Tup>
T create( std::index_sequence<Offs...>, Tup&& tup ) {
return T( std::get<I+Offs>(std::forward<Tup>(tup))... );
}
template <unsigned N>
struct Pixel {
float color[N];
template<class...Ts,
std::enable_if_t< sizeof...(Ts)==N, bool > = true
>
Pixel(Ts&&...ts):color{ std::forward<Ts>(ts)... } {};
};
template <unsigned N>
struct PixelContainer {
std::vector<Pixel<N>> container;
template<class T0, class...Ts,
std::enable_if_t<!std::is_same<std::decay_t<T0>, PixelContainer>{}, bool> =true
>
PixelContainer(T0&& t0, Ts&&...ts):
PixelContainer( std::make_index_sequence<(1+sizeof...(Ts))/N>{}, std::forward_as_tuple( std::forward<T0>(t0), std::forward<Ts>(ts)... ) )
{}
PixelContainer() = default;
private:
template<class...Ts, std::size_t...Is>
PixelContainer( std::index_sequence<Is...>, std::tuple<Ts&&...>&& ts ):
container{ create<Pixel<N>, Is*N>( std::make_index_sequence<N>{}, std::move(ts) )... }
{}
};
create takes a type, a starting index, and a index sequence of offsets. Then it takes a tuple.
It then creates the type from the (starting index)+(each of the offsets) and returns it.
We use this in the private ctor of PixelContainer. It has an index sequence element for each of the Pixels whose elements are in the tuple.
We multiply the index sequence element by N, the number of elements per index sequence, and pass that to create. Also, we pass in an index sequence of 0,...,N-1 for the offsets, and the master tuple.
We then unpack that into a {} enclosed ctor for container.
The public ctor just forwards to the private ctor with the right pack of indexes of one-per-element (equal to argument count/N). It has some SFINAE annoyance enable_if_t stuff to avoid it swallowing stuff that should go to a copy ctor.
Live example.
Also,
std::enable_if_t<0 == ((sizeof...(Ts)+1)%N), bool> =true
could be a useful SFINAE addition to PixelContainer's public ctor.
Without it, we simply round down and discard "extra" elements passed to PixelContainer. With it, we get a "no ctor found" if we have extra elements (ie, not a multiple of N).
Made something as well, which relies more on compiler optimizations for performance than #Yakk's answer.
It uses temporary std::arrays. temp is used to store the passed values somewhere. temp_pixels is used to copy pixel data from temp. Finally temp is copied into container.
I believe that those arrays do get optimized away, but I'm not certain. Looking at godbolt it seems that they are but I am not good at reading compiler assembly output :)
#include <array>
#include <algorithm>
#include <cstddef>
#include <vector>
template <unsigned N>
struct Pixel {
float color[N]; // consider std::array here
};
template <unsigned N>
class PixelContainer {
std::vector<Pixel<N>> container;
public:
template<class... Ts>
PixelContainer(Ts... values)
{
static_assert(sizeof...(Ts) % N == 0, "Pixels should be grouped by 3 values in PixelContainer constructor");
const std::array<float, sizeof...(Ts)> temp{float(values)...};
std::array<Pixel<N>, sizeof...(Ts) / N> temp_pixels{};
for (std::size_t i = 0; i < sizeof...(Ts); i += N)
{
auto& pixel = temp_pixels[i / N];
std::copy(
temp.begin() + i, temp.begin() + i + N,
pixel.color
);
}
container = std::vector<Pixel<N>>(temp_pixels.begin(), temp_pixels.end());
}
};
int main()
{
PixelContainer<3> pc1(1, 2, 3); // Creates a container containing one Pixel<3> objects
PixelContainer<3> pc2(1, 2, 3, 4, 5, 6); // Creates a container containing 2 Pixel<3> objects
PixelContainer<2> pc3(1, 2, 3, 4, 5, 6); // Creates a container containing 3 Pixel<2> objects
/*
PixelContainer<3> pc4(2, 3); // Not enough arguments
PixelContainer<2> pc5(1, 2, 3, 4, 5); // Too many arguments
*/
}
I would propose some hybrid version, there is still a temporary array, but no temporary for pixels
template <unsigned N>
struct PixelContainer {
template<std::size_t S, std::size_t... elts>
auto createOne(const std::array<float, S> &arr, std::size_t offset,
std::index_sequence<elts...>) {
return Pixel<N>{ arr[offset + elts]... };
}
template<typename... Floats>
PixelContainer(Floats... vals) {
static_assert(sizeof...(vals) % N == 0, "invalid number of args");
std::array<float, sizeof...(vals)> arr = { float(vals)... };
for (size_t i = 0; i < sizeof...(vals) / N; i++) {
container.push_back(createOne(arr, i * N, std::make_index_sequence<N>{}));
}
}
std::vector<Pixel<N>> container;
};
I think the answer is pretty much given in the link you provided (C++ number of function's parameters fixed by template parameter). You just need to change the assert there: instead of sizeof...(Floats) == N you'll want sizeof...(Floats) % N == 0.

C++ 14: How to use variadic template to create an array of values 1-100

I wish to get an array of values int buf[]={1...100}. I wish this array can be constructed at compile time, using variadic template. This is like list comprehension of Python/Haskell, etc.
But can c++11/14 template do it, and how?
Thanks
C++14 allows loops at compile time.
constexpr auto make_upto_100() {
std::array< int, 100 > ret = {};
for ( int i = 0; i != 100; ++ i ) ret[i] = i + 1;
return ret;
}
C++11 allows a utility like make_index_sequence which might be more like your thinking. (C++14 also has std::[make_]index_sequence.)
template< std::size_t ... i >
struct index_sequence
{ typedef index_sequence< i ..., sizeof ... (i) > next; };
template< std::size_t last >
struct index_seq_maker
{ typedef typename index_seq_maker< last - 1 >::type::next type; };
template<>
struct index_seq_maker< 0 >
{ typedef index_sequence<> type; };
template< std::size_t n >
using make_index_sequence = typename index_seq_maker< n >::type;
template< int ... i >
constexpr
std::array< int, 100 >
make_upto_100( index_sequence< i ... > )
{ return {{ i + 1 ... }}; }
constexpr
std::array< int, 100 > upto_100() = make_upto_100( make_index_sequence< 100 >{} );
If you are really set on making this at compile-time. You could do it with an integer_sequence and a std::array
#include <utility>
#include <array>
template <int... Is> // when called below, Is will be 0 - N
constexpr std::array<int, sizeof...(Is)> make_inc_array_impl(
std::integer_sequence<int, Is...>) {
return {{(Is + 1)...}}; // +1 to start at one instead of [0, 1, ...]
}
template <std::size_t N>
constexpr std::array<int, N> make_inc_array() {
return make_inc_array_impl(std::make_integer_sequence<int, N>{});
}
Then call with your size
constexpr auto a = make_inc_array<100>(); // [1, 2, ..., 100]
This is far less flexible than a list comprehension, and you're probably way better off just using std::iota and initializing at runtime.
Well, it's not compile time, but typically, I'd expect to most code to use std::iota. This might actually be faster than compile-time magic in some cases, since compile time arrays would need to be stored in the executable's .data segment; if the array is large enough, reading the extra disk pages in from .data might end up slower than writing to purely-in-memory pages that are pulled fresh from the OS.
Simple usage would be:
int buf[100];
std::iota(&buf[0], &buf[100], 1);
Frankly, I'd start here, and only start looking at template magic if you have a proven performance issue with runtime initialization.
This should work for c++14. It works by recursive template instanciation to initialize all the values as a constexpr. You should be able to change the size of sequential values to whatever you need by changing the template parameter. Note for very large arrays it could hit the recursion limit:
#include <array>
template<int NumVal, int ArrSize>
constexpr void setVal(std::array<int, ArrSize> &constArr) {
std::get<NumVal>(constArr) = NumVal + 1;
if(NumVal) setVal<NumVal ? NumVal - 1 : 0, ArrSize>(constArr);
}
template<int ArrSize>
constexpr auto arrRange() -> std::array<int, ArrSize> {
std::array<int, ArrSize> tmp{};
setVal<ArrSize - 1, ArrSize>(tmp);
return tmp;
}
constexpr std::array<int, 100> constArr = arrRange<100>();
int main() {
for(int itr = 0; itr < 100; ++itr) printf("%d ", constArr[itr]);
}

fill static templated arrays with metaprogramming and variadic templates

I know that there are easier ways to do it, but
I would like to initialize at compilation time
the map from unrolled index of 2d array to its general format.
I would like to do this without needing to instansiate the array object.
Below I define the map from array[][]->array[].
Now I wonder how to do the opposite: [] -> [][]
without hardcoding the chosen mapping scheme.
I guess that should be possible using metaprogramming and variadic templates.
But I tried using it for the first time just a couple of days ago,
so it takes a while to get used to ;)
header:
template <int dim>
class internal {
static unsigned int table[dim][dim];
static unsigned int x_comp[dim*dim];
static unsigned int y_comp[dim*dim];
};
source:
//1d case:
template <>
unsigned int
internal<1>::table[1][1] = {{0}};
template <>
unsigned int
internal<1>::x_component[1] = {0};
template <>
unsigned int
internal<1>::y_component[1] = {0};
//2d case:
template<>
unsigned int
internal<2>::table[2][2] =
{{0, 1},
{2, 3}
};
// here goes some metaprogramming tricks to initialize
// internal<2>::y_component[2*2] = ...
// internal<2>::x_component[2*2] = ...
// based on mapping above, i.e. table[2][2];
// that is:
// x_table = { 0, 0, 1, 1 }
// y_table = { 0, 1, 0, 1 }
//
// so that :
//
// index == table[i][j]
// i == x_comp[index]
// j == y_comp[index]
EDIT1:
or just tell me that it's not possible and I hard-code everything or use
integer division to relate the two index representations.
EDIT2:
i would prefer to stick with definition of arbitrary arrays.
Of course one can do without, as in answer below using integer division.
Those arrays can be really arbitrary, for example:
template<>
unsigned int
internal<2>::table[2][2] =
{{3, 0},
{2, 1}
};
Using arrays:
Given a table with unique entries from 0 to dim^2-1, you can write constexpr lookup functions for the i and j of a given table entry:
constexpr unsigned get_x_comp(unsigned index, unsigned i=0, unsigned j=0)
{ return table[i][j] == index ? i : get_x_comp(index, ((j+1)%dim ? i : i+1), (j+1)%dim); }
constexpr unsigned get_y_comp(unsigned index, unsigned i=0, unsigned j=0)
{ return table[i][j] == index ? j : get_y_comp(index, ((j+1)%dim ? i : i+1), (j+1)%dim); }
These will recursively call themselves, iterating through the table and looking for index. Recursion will eventually end when the given index is found and i/j of that index is returned.
Combine that with the C++14 std::integer_sequence mentioned by Jonathan to initialize the arrays:
template<unsigned... I>
constexpr auto make_x_comp(std::integer_sequence<unsigned, I...>) -> std::array<unsigned, sizeof...(I)> { return {get_x_comp(I)...}; }
Using metafunctions instead of arrays:
In some cicumstances, one might not even need arrays. I assume you want to the table to contain consecutive indices from 0 to dim^2-1. If that's the case, table, x_comp and y_comp are only simple compiletime functions with the following attributes:
table(i,j) := i*dim + j
x_comp(index) := index / dim (integer division)
y_comp(index) := index % dim
Depending on if you have C++11 features available, the implementation will be different, but both times without arrays.
Note: the following implementations will assume that the numbers stored in table are consecutive from 0 to dim^2-1. If that is not the case, you'll have to roll your own appropiate function for table and use the above get_x_comp and get_y_comp implementatio
C++11:
template <unsigned dim> //use unsigned to avoid negative numbers!
struct internal {
static constexpr unsigned table(unsigned i, unsigned j) { return i*dim+j; }
static constexpr unsigned x_comp(unsigned index) { return index/dim; }
static constexpr unsigned y_comp(unsigned index) { return index%dim; }
};
You can call these functions like normal functions anywhere, especially anywhere you need compiletime constants. Example: int a[internal<5>::table(2,4)];
C++03:
template <unsigned dim> //use unsigned to avoid negative numbers!
struct internal {
template<unsigned i, unsigned j>
struct table{ static const unsigned value = i*dim+j; };
template<unsigned index>
struct x_comp{ static const unsigned value = index/dim; };
template<unsigned index>
struct y_comp{ static const unsigned value = index%dim; };
};
Using these metafunctions is a bit more clumsy than in C++11, but works as usual with template metafunctions. Same example as above: int a[internal<5>::table<2,4>::value];
Note: This time you can put the (meta-)functions in the header, since they are not non-integral static member variables any more. Also you do not need to restrict the template to small dimensions, since everything will be calculated well for dimensions less than sqrt(numeric_limits<unsigned>::max()).
I'm sorry if I'm not answering the question directly (or at all), but I don't really understand what you're asking. I think what you're saying is that you want to initialize at compilation time a way to have an array of size N x M represented as a 1D array?
I've included code that allows you to allocate non-square dimensions. I've built this in "easy" C++ so if you're just getting into templates it's not so difficult to follow.
Is it possible to do something like this?
template <typename T, typename std::size_t N, typename std::size_t M = 1>
class Array {
T* data;
public:
Array<T, N, M>() : data(new T[N * M]) {
T temp = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
data[i * M + j] = temp++;
}
}
}
/* methods and stuff
}
Where M is the column number, so you'd use this like:
int main(void) {
Array<float, 10, 10> myArray;
return 0;
}
Remember to call delete in the destructor.
Edit: I didn't understand the rule for populating x_comp and y_comp when I wrote this, now that I see that part of the question this answer is not really relevant, because I was incorrectly assuming table only contained consecutive integers. The answer is left here anyway because Arne's (much better) answer refers to it.
I would replace the arrays with std::array and use the C++14 integer_sequence utility:
template <int dim>
struct internal {
static std::array<std::array<unsigned, dim>, dim> table;
static std::array<unsigned, dim*dim> x_comp;
static std::array<unsigned, dim*dim> y_comp;
};
template<unsigned Origin, unsigned... I>
constexpr std::array<unsigned, sizeof...(I)>
make_1d_array_impl(std::integer_sequence<unsigned, I...>)
{
return { { I + Origin ... } };
}
template<int N>
constexpr std::array<unsigned, N*N>
make_1d_array()
{
return make_1d_array_impl<0>(std::make_integer_sequence<unsigned, N*N>{});
}
template<unsigned... I>
constexpr std::array<std::array<unsigned, sizeof...(I)>, sizeof...(I)>
make_2d_array_impl(std::integer_sequence<unsigned, I...> seq)
{
return { { make_1d_array_impl<I*sizeof...(I)>(seq) ... } };
}
template<int N>
constexpr std::array<std::array<unsigned, N>, N>
make_2d_array()
{
return make_2d_array_impl(std::make_integer_sequence<unsigned, N>{});
}
template<int dim>
std::array<std::array<unsigned, dim>, dim> internal<dim>::table = make_2d_array<dim>();
That fills the table array correctly. I'll have to think about it a bit more to populate x_comp and y_comp as you want, but it's doable.
You can find an C++11 implementation of integer_sequence at https://gitlab.com/redistd/integer_seq/blob/master/integer_seq.h