How to find size of multidimensional std::array? - c++

I am having a multidimensional std::array and looking for the proper (convenient and efficient) way to find its size (in number of cells or in bytes)
size() returns only the last dimension size (which is understood)
std::array<std::array<std::array<std::array<double, 2>, 3>, 6>, 100> my_array;
my_array.size(); // returns 100
Although the size of the array is known in compile time, I wish to avoid #define SIZE_OF_MY_ARRAY or a global variable because, I am looking for a better way than passing the size with the array (as in "old" arrays, not std array), or informing others to use the defined term.
I also prefer no to calculate it every time.
Perhaps these 2 preferences are not possible for multidimensional array?
How can I efficiently find the overall size of my_array? I am using c++11.

It's not too hard to write a small utility that calculates this at compile time.
template<typename T> struct arr_sz {
static constexpr std::size_t size = sizeof(T);
};
template<typename T, std::size_t N>
struct arr_sz<std::array<T, N>> {
static constexpr std::size_t size = N * arr_sz<T>::size;
};
The above should unravel a nested array definition of any practical depth, and evaluate to the size in bytes used for storing T's, with all potential padding excluded.
An example of using it would be
std::array<std::array<std::array<std::array<double, 2>, 3>, 6>, 100> my_array;
constexpr auto sz = arr_sz<decltype(my_array)>::size;
static_assert(sz == sizeof(double) * 2 * 3 * 6 * 100, "");
That you may see live.

How about
sizeof(my_array)/sizeof(double);

You can use a recursion to calculate its size:
template<typename T>
size_t get_array_full_size(T &val) { // for the last array type (double in this case)
return 1;
}
template<typename T, size_t Len>
size_t get_array_full_size(std::array<T, Len> &arr) {
return get_array_full_size(arr[0]) * arr.size();
}
int main() {
std::array<std::array<std::array<std::array<double, 2>, 3>, 6>, 100> my_array;
std::cout << get_array_full_size(my_array) << std::endl;
return 0;
}

You could use constexpr variables that bind to the dimensions and then use those to compute the desired quantity.
constexpr int n1 = 2;
constexpr int n2 = 3;
constexpr int n3 = 6;
constexpr int n4 = 100;
std::array<std::array<std::array<std::array<double, n1>, n2>, n3>, n4> my_array;
constexpr int myArraySize = n1*n2*n3*n4;

Something similar to #KorelK 's approach. Recursive template function will stop when
class template of std::array is primitive type.
(See online)
#include <array>
#include <type_traits> // std::is_fundamental_v
template<typename Type, std::size_t N>
std::size_t overall_size(const std::array<Type, N>&)
{
if constexpr(std::is_fundamental_v<Type>) return N;
else return N * overall_size(Type{});
}

Related

How to initialize array elements with their indices

I would like to find an elegant way of initializing C++ array elements with their indices. I have a lot of code that looks like this:
static constexpr size_t ELEMENT_COUNT = 8;
MyObject x[ELEMENT_COUNT] = {{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}};
Where MyObject is effectively,
struct MyObject {
size_t mMyIndex;
MyObject(size_t myIndex) : mMyIndex(myIndex) {}
};
The problems should be already clear: when ELEMENT_COUNT changes, I have to adapt the initializer list, which feels very un-C++. And if ELEMENT_COUNT is, say, 1000, this becomes impractical.
Is something like the following possible in C++?:
MyObject mObjects[ELEMENT_COUNT] = initialize_array_with_indices<ELEMENT_COUNT>();
Does such a function like initialize_array_with_indices<N>() exist in the std library? Is it possible? Using std::array<> is an option, if that gets me further.
It is impossible to intialize a built-in array like this. Arrays can only be default-initialized, value-initialized or aggregate-initialized (with exception for string literals). The only one of these allowing to specify different values for the elements is aggregate initialization and that requires explicitly listing each element.
(There is one other exception specific to non-static array members of classes. They may be initialized by copy through implicitly defined constructors of the enclosing class. However that still doesn't allow writing an initializer like you want.)
So you have to use std::iota or a loop after the initialization.
If you use std::array instead of a built-in array, you can define it as
template<typename T, std::size_t N>
constexpr auto initialize_array_with_indices() {
return []<std::size_t... Is>(std::index_sequence<Is...>){
return std::array<T, N>{Is...};
}(std::make_index_sequence<N>());
}
To be used like
auto mObjects = initialize_array_with_indices<MyObject, ELEMENT_COUNT>();
The implementation above requires C++20. It can be written (slightly longer) for previous versions as well. Specifically before C++20 lambdas can't have explicit template parameters, so a helper function must be used instead (or a constexpr array of indices can be filled first with the approach below and then std::apply used on it to get the indices as a pack into the lambda). Also before C++17 it will require copy/move-constructibility of T.
An implementation that assumes that default-initialization of MyObject is possible and not undesirable would be much more straight-forward. (It would simply default-initialize a std::array and then loop through it to set the indices or use std::iota on it.)
Here is another way of doing this with both C++11 as well as C++20. Note that starting with C++17 we can do all this at compile time(if required). Additionally, I've made the converting constructor constexpr.
C++11 Version
This uses SFINAE:
struct C
{
int i;
//added constexpr here
constexpr C(int pi): i(pi)
{
}
};
/*
Version #1 : Used to end the recursion.
#tparam: destType : represents type of elements in the array to which we want to convert to. This parameter must be explicitly passed in angle brackets
#tparam: sourceType: represents type of elements in the array from which we want to convert. This parameter will be deduced using argument deduction
#tparam: sourceSize: represents size of the source array. This will also be deduced using deduction
#tparam: extractedTypes represents type of the values extracted by indexing and casting individual elments of sourceArray
#return: this function template returns an std::array with elements of type destType and size sourceSize
*/
template<typename destType, typename sourceType, std::size_t sourceSize, typename... extractedTypes>
constexpr typename std::enable_if<sizeof...(extractedTypes) == sourceSize, std::array<destType, sourceSize> >::type
cast_array(const std::array<sourceType, sourceSize> &sourceArray, const extractedTypes... extractedValues)
{
return std::array<destType, sourceSize>{{extractedValues...}};//use pack expansion
}
/*
Version 2 : Recursively called until sizeof...(Types) becomes same as sourceSize
#tparam: destType : represents type to be converted to which must be explicitly passed in angle brackets
#tparam: sourceType: represents type of elements in the array from which we want to convert. This will be deduced using argument deduction
#tparam: sourceSize: represents size of the source array. This will also be deduced using deduction
#tparam: extractedTypes represents type of the values extracted by indexing and casting individual elments of sourceArray
#return: this function template returns an std::array with elements of type destType and size sourceSize
*/
template<typename destType, typename sourceType, std::size_t sourceSize, typename... extractedTypes>
constexpr typename std::enable_if<sizeof...(extractedTypes) != sourceSize, std::array<destType, sourceSize> >::type
cast_array(const std::array<sourceType, sourceSize> &sourceArray, const extractedTypes... extractedValues)
{
//recursively call cast_array passing the sourceArray and extracted elements until all elements are extracted
return cast_array<destType>(sourceArray, extractedValues..., static_cast<destType>(sourceArray[sizeof...(extractedValues)]));
}//-----------------------------------------------------------------------------------------------^^^^^^^^^^^^^^^^->extract/pass elements index by index by casting them to destType
static constexpr size_t ELEMENT_COUNT = 8;
template<std::size_t N = ELEMENT_COUNT>
std::array<C, N> make_array()
{
std::array<int, ELEMENT_COUNT> tempArray{};
int count = 0;
for(auto &elem: tempArray)
{
elem = ++count;
}
return cast_array<C>(tempArray);
}
int main()
{
std::array<C, ELEMENT_COUNT> myArray = make_array();
//lets confirm if all objects have the expected value
for(const auto &elem: myArray)
{
std::cout << elem.i << std::endl;;
}
}
Working demo C++11
C++17 Version
With C++17, std::array<T, N>::begin is constexpr, so we can do all this at compile time(if needed). Here no changes have been made to the cast_array function template. Instead constexpr is added to make_array and myArray. So to avoid pasting the same code again and again, in the below code snippet I've only pasted the modified portion of the code. You can check out the full working example here.
//--------------------------------------vvvvvvvvv------->added constexpr here
template<std::size_t N = ELEMENT_COUNT> constexpr std::array<C, N> make_array()
{
std::array<int, ELEMENT_COUNT> tempArray{};
int count = 0;
for(auto &elem: tempArray)
{
elem = ++count;
}
return cast_array<C>(tempArray);
}
int main()
{
//note the constexpr here from C++17
constexpr std::array<C, ELEMENT_COUNT> myArray = make_array();
//lets confirm if all objects have the expected value
for(const auto &elem: myArray)
{
std::cout << elem.i << std::endl;;
}
}
C++17 demo
C++20 Version
Here we make use of requires. Since changes have been done to cast_array also so I will post the whole code here again(except the documentation part)
struct C
{
int i;
//added this constexpr
constexpr C(int pi): i(pi)
{
}
};
template<typename destType, typename sourceType, std::size_t sourceSize, typename... extractedTypes>
constexpr std::array<destType, sourceSize> cast_array(const std::array<sourceType, sourceSize> &sourceArray, const extractedTypes... extractedValues)
requires (sizeof...(extractedTypes) == sourceSize)
{
return std::array<destType, sourceSize>{{extractedValues...}};//use pack expansion
}
template<typename destType, typename sourceType, std::size_t sourceSize, typename... extractedTypes>
constexpr std::array<destType, sourceSize> cast_array(const std::array<sourceType, sourceSize> &sourceArray, const extractedTypes... extractedValues)
requires (sizeof...(extractedTypes) != sourceSize)
{
//recursively call cast_array passing the sourceArray and extracted elements until all elements are extracted
return cast_array<destType>(sourceArray, extractedValues..., static_cast<destType>(sourceArray[sizeof...(extractedValues)]));
}//-----------------------------------------------------------------------------------------------^^^^^^^^^^^^^^^^->extract/pass elements index by index by casting them to destType
static constexpr size_t ELEMENT_COUNT = 8;
//--------------------------------------vvvvvvvvv------->added constexpr here
template<std::size_t N = ELEMENT_COUNT> constexpr std::array<C, N> make_array()
{
std::array<int, ELEMENT_COUNT> tempArray{};
int count = 0;
for(auto &elem: tempArray)
{
elem = ++count;
}
return cast_array<C>(tempArray);
}
int main()
{
constexpr std::array<C, ELEMENT_COUNT> myArray = make_array();
for(const auto &elem: myArray)
{
std::cout << elem.i << std::endl;;
}
}
Working demo C++20
Note
If this is a one time thing then you can also use a lambda instead of a separate function template make_array. See demo with lambda
The answer from #user17732522 is fantastic and works in C++20. As they state, it only works for >=C++20, but with adaptations it can work for C++17 and earlier.
Here is a C++17 version, using a helper function:
template<typename T, std::size_t N, std::size_t... Is>
std::array<T, N> initialize_array_with_indices_helper(std::index_sequence<Is...>) {
return std::array<T, N>{Is...};
}
template<typename T, std::size_t N>
constexpr std::array<T,N> initialize_array_with_indices() {
return initialize_array_with_indices_helper<T, N>(std::make_index_sequence<N>());
}

Filling an std::array using math formula in compile time

I want to fill a constexpr std::array in compile time using a math function. Is it possible in an easy way?
I found this solution: C++11: Compile Time Calculation of Array. However, is there any other modern solution using only std? This one seems too confusing for me.
int main()
{
// This is the array I need to fill
constexpr std::array<double, 100000> elements;
for (int i=0; i!=100000; ++i)
{
// Each element is calculated using its position with long exponential maths.
elements[i] = complexFormula(i); // complexFormula is constexpr
}
double anyVal = elements[43621];
// ...
}
Here's a non-confusing approach: wrap the calculation in a function:
template <int N>
constexpr std::array<double, N> generate()
{
std::array<double, N> arr{};
for (int i = 0; i < N; ++i)
arr[i] = complexFormula(i);
return arr;
}
Usage example:
constexpr std::array<double, 10000> arr = generate<10000>();
(live demo)
This works because, since C++14, loops are allowed in a constexpr function, and variables can be modified as long as their lifetime starts within the evaluation of the constant expression.
In c++14 you also have the possibility of using std::index_sequence for your purpose:
template <std::size_t... I>
constexpr std::array<double, sizeof...(I)> generate_impl(std::index_sequence<I..>) noexcept
{
return { complexFormula(I)... };
}
template <std::size_t N>
constexpr std::array<double, N> generate() noexcept
{
return generate_impl(std::make_index_sequence<N>{});
}

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]);
}

Programmatic initialization of a long array at compile or initialization time

Here is a code snippet illustrating my question:
const float D = 0.1F;
const float A[4] = {sin(0*D), sin(1*D), sin(2*D), sin(3*D)};
Imagine that global array A is much longer and you don't want to do all of this repetitive typing. Is there a shorter way to initialize array A at compile or initialization time, i.e. without having to write initialization function and call it somewhere in my program?
You could initialize A during dynamic initialization time as follows:
const float *init_a(float x_)
{
static float data[4];
for(unsigned i=0; i<4; ++i)
data[i]=sin(i*x_);
return data;
}
const float D=0.1f;
const float *A=init_a(D);
You may use code generator to generate initialization code. That is, write program that will write your initialization code for you. You may actually calculate values at generation-time.
Remember that C++ allows placing , after last element. It's also isn't necessary to specify array size. These two things should ease writing of generator.
This simple python code should work well:
from math import sin
print('const float A[', N, '] = {')
for i in range(N):
print('\t', sin(i*D), ',', sep='')
print('};')
Ok I just realized this doesn't actually answer the question, because it specifies "without having to write initialization function and call it somewhere in my program?" But I can't think of a convenient alternative.
template<size_t N>
std::array<float, N> GetLookupTable(float d)
{
std::array<float, N> table;
// .. populate table here
return table;
}
// a global somewhere
(static?) const auto Table_10x5 = GetLookupTable<10>(5.0f);
This first part is obsolete in C++14, but not long:
template<unsigned...>struct indexes { using type=indexes; };
template<unsigned Max, unsigned...Is>struct make_indexes:make_indexes<Max-1,Max-1,Is...>{};
template<unsigned...Is>struct make_indexes<0,Is...>:indexes<Is...>{};
template<unsigned Max>using make_indexes_t=typename make_indexes<Max>::type;
this is some template meta programming that lets us create and pass around bundles of indexes.
Then some code to generate the array:
namespace details {
template<std::size_t N, unsigned...Is>
std::array<float, N> poly_sin(float src, indexes<Is...>) {
return { (Is*sin(src))... };
}
}
template<std::size_t N>
std::array<float, N> poly_sin(float src) {
return details::poly_sin<N>( src, make_indexes_t<N>{} );
}
The first method takes indexes<Is...> and we plan that Is... is 0, 1, 2, ..., N-1. It then expands the parameter pack into a std::array of the right size.
make_indexes_t<N>{} expands (at compile time) to indexes<0, 1, 2, ..., N-1>{}, which is then passed to details::poly_sin, which then can deduce the Is... and use them within itself.
And point of use:
const float D = 0.1F;
const auto A = poly_sin<4>(D);
if you had a constexpr sin function, you could even make poly_sin a constexpr function and have it basically guaranteed to be evaluated at compile time.
If this is the case, make D constexpr and same with the two poly_sin functions.
As written, this occurs at dynamic initialization time.
While it appears that the array is copied twice, RVO elision means that any decent compiler will directly construct it in A.
If you want to be able to do this in general, first start with the above indexes code. Then add this:
template<class Sig>using result_of_t=typename std::result_of<Sig>::type;
namespace details {
template<std::size_t N, class F, unsigned... Is>
std::array< result_of_t< F(unsigned) >, N >
make_array( F&& f, indexes<Is...> ) {
return { f( Is )... };
}
}
template<std::size_t N, class F>
std::array< result_of_t< F(unsigned) >, N >
make_array( F&& f ) {
return details::make_array( std::forward<F>(f), make_indexes_t<N>{} );
}
const auto A = make_array<4>( [](unsigned i){ return float(i*sin(D)); } );
which uses a lambda to pass in the code that is repeated to build the array. Sadly, lambdas are not by default constexpr so you cannot do it at compile time.
You could either use boost.preprocessor and in particular the BOOST_PP_ENUM macro, like the example below:
#include <iostream>
#include <cmath>
#include <boost/preprocessor/repetition/enum.hpp>
#define SIZE 4
#define D 0.1
#define ORDER(z, n, text) std::sin(n * D)
double const A[SIZE] = { BOOST_PP_ENUM(SIZE, ORDER, ~) };
int main() {
for(auto i : A) std::cout << i << std::endl;
}
Or, you could use std::array instead of raw arrays, and via use of template meta-programming to generate a std::array at compile time. like the example below:
template<typename T, typename F, int SIZE, int... N>
constexpr std::array<T, SIZE>
genarray(F f) {
return std::array<T, SIZE>{{ f(N)... }};
}
template<typename T, typename F, int SIZE, int...> struct recursive_gen;
template<typename T, typename F, int SIZE, int... Args>
struct recursive_gen<T, F, SIZE, 0, Args...> {
static constexpr std::array<T, SIZE> generate(F f) {
return genarray<T, F, SIZE, 0, Args...>(f);
}
};
template<typename T, typename F, int SIZE, int N, int... Args>
struct recursive_gen<T, F, SIZE, N, Args...> {
static constexpr std::array<T, SIZE> generate(F f) {
return recursive_gen<T, F, SIZE, N - 1, N, Args...>::generate(f);
}
};
template<typename T, int SIZE>
struct array_generator {
template<typename F>
static constexpr std::array<T, SIZE> generate(F f) {
return recursive_gen<T, F, SIZE, SIZE - 1>::generate(f);
}
};
std::array<double, 4> const A = array_generator<double, 4>::generate([](int i) { return std::sin(0.1 * i);});
std::array<double, 4> const B = array_generator<double, 4>::generate([](int i) { return std::cos(0.1 * i);});
constexpr int fun(int i) { return 2 * i; }
constexpr std::array<int, 4> const C = array_generator<int, 4>::generate(fun); // generation during compile time
LIVE DEMO
Note however, that in order for generation to take place at compile time input function in array_generator must be constexpr. This is not the case for trigonometric functions (i.e., they are not constexpr). Thus initialization of arrays A and B will take place at initialization time, whereas generation of array C will take place at compile time.
Imagine that global array A is much longer and you don't want to do all of this repetitive typing. Is there a shorter way to initialize array A at compile or initialization time
Create a generator and pass it through std::generate_n() (or plain std::generate()).
#include <algorithm>
#include <array>
#include <cmath>
template <typename Value_t>
struct SinGenerator{
SinGenerator(std::size_t start = 0, Value_t counter_scalar = 1)
: index{start},
scalar{counter_scalar} {
}
Value_t operator()() {
return sin(index++ * scalar);
}
std::size_t index;
Value_t scalar;
};
template <typename Value_t, std::size_t Size>
std::array<Value_t, Size> init_table(const std::size_t start,
const Value_t counter_scalar) {
std::array<Value_t, Size> arr;
SinGenerator<Value_t> gen(start, counter_scalar);
std::generate(arr.begin(), arr.end(), gen);
return arr;
}
const auto kSinTable(init_table<float, 10>(0, 0.1f));
In case you A array will always stays the same and is very big, you can always write a short script, which calculates every value in array and output could be used in source code to have static initialization.
In case formula is simple, even MS Excel could be used to generate that kind of static initialization data.

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