Related
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>());
}
I am looking for a convenient and efficient way to initialize a static array in a function template. For example, let's say we have a function like this:
template <size_t windowSize>
int process_signal(int currentSignal)
{
// incorrect: inits only 3 elements; want to set all values to -1
static std::array<int, windowSize> window = { -1, -1, -1 };
// do something...
}
This way I can initialize only first 3 elements with -1, but not all of them.
The best solution I've come up so far is using constexpr function for compile-time initialization:
template <size_t sz, int value>
constexpr std::array<int, sz> init_array()
{
std::array<int, sz> arr{};
//arr.fill(-1); // will be constexpr since C++20
// operator [] is constexpr in C++17
for (int i = 0; i < sz; ++i)
arr[i] = value;
return arr;
}
template <size_t windowSize>
int process_signal(int currentSignal)
{
// works fine, but extra variable needed
constexpr static std::array<int, windowSize> init_value = init_array<windowSize, -1>();
static std::array<int, windowSize> window = init_value;
// window will be updated...
}
This way I can initialize the array once at compile-time. However, it requires an additional constexpr variable (in my case init_value) to bind the result of init_array() function. And if I try the same without it, init_array() is called as a normal function (so initialization is no longer compile-time):
template <size_t windowSize>
int process_signal(int currentSignal)
{
// no longer compile-time in MSVC2019
static std::array<int, windowSize> window = init_array<windowSize, -1>();
// window will be updated...
}
Is it possible to do the same without extra constexpr variable? Or, maybe, it is just specific to my compiler (MSVC 2019)?
There is no need for your extra variable, just write:
static auto window = init_array<windowSize, -1>();
The initializer is still a constant expression and should be evaluated by the compiler at compile-time.
If your compiler is not optimizing this properly for some reason, then you can use a lambda to store the intermediate constexpr variable:
static auto window = []{ constexpr auto x = init_array<windowSize, -1>(); return x; }();
or you can put this in a separate function, e.g. in init_array:
template <size_t sz, int value>
constexpr std::array<int, sz> init_array()
{
constexpr auto x = []{
std::array<int, sz> arr{};
//arr.fill(-1); // will be constexpr since C++20
// operator [] is constexpr in C++17
for (int i = 0; i < sz; ++i)
arr[i] = value;
return arr;
}();
return x;
}
You can make the function much more generic:
template <std::size_t S, typename T>
constexpr auto init_array(const T& value)
{
return std::apply([&](auto... pack){
return std::array{((void)pack, value)...};
}, std::array<int, S>{});
}
used as
static auto window = init_array<windowSize>(-1);
or
template <std::size_t S, auto value>
constexpr auto init_array()
{
constexpr auto x = std::apply([](auto... pack){
return std::array{((void)pack, value)...};
}, std::array<int, S>{});
return x;
}
used as
static auto window = init_array<windowSize, -1>;
to force compile-time evaluation (up to copying).
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{});
}
Is it possible to declare a const array (possibly constexpr) at one point, then define it at another place, one element at a time?
E.g.
extern constexpr int myArray[100];
myArray[0] = myConstexprFunction(0);
myArray[1] = myConstexprFunction(1);
// ...
myArray[100] = myConstexprFunction(100);
What I'm trying to do will need something like this. Maybe it's possible using things like: http://b.atch.se/posts/constexpr-counter/
But if this technique is going to be illegal in the next C++ standard (I hope not) I would like to use a safer one.
[EDIT] how about relaxing some requirements.. let's say that I want to do something like this:
constexpr int myConstExprFunction(int arg) { return arg + 100;}
// other code...
constexpr int a = myConstExprFunctionBegin(10);
constexpr int b = myConstExprFunction(20);
constexpr int c = myConstExprFunction(30);
constexpr int d = myConstExprFunctionEnd(40);
what I would like to have is that the myConstExprFunctionEnd is able to generate a final array with the values created by the previous functions.
Everything at compile time of course.
[EDIT2] C++11 solutions very welcomed
The requirement of constexpr of the recent C++ is very relaxed, so you could just write:
// requires C++17:
constexpr auto myArray = [] {
std::array<int, 100> result {};
for (size_t i = 0; i < 100; ++ i) {
result[i] = i * i;
}
return result;
}();
Note I used std::array<int, 100> instead of int[100] because a function cannot return a C array.
The above code requires C++17 for two reasons:
constexpr lambda
The mutable operator[] is not constexpr before C++17
Issue 1 can be easily worked-around using a separate constexpr function. Issue 2 can only be solved by defining your own array wrapper.
// requires C++14:
template <typename T, size_t n>
struct ConstexprArray {
T data[n];
constexpr ConstexprArray() : data{} {}
constexpr T& operator[](size_t i) { return data[i]; }
};
constexpr auto initialize_my_array() -> ConstexprArray<int, 100> {
ConstexprArray<int, 100> result {};
for (size_t i = 0; i < 100; ++ i) {
result[i] = i * i;
}
return result;
}
constexpr auto myArray = initialize_my_array();
Looking at your edit, I'd just answer no, because the compiler cannot transform a group of variables into an array. It just don't work that way. There isn't any construct in C++ that can take a bunch of declaration, delete them and replace it with another declaration. A source code preprocessor or generator might be able to permit the syntax you seek.
If you're interested in a solution that doesn't require external tooling, you can create a constexpr function that returns an array:
constexpr auto makeMyArray() {
std::array<int, 100> myArray{};
myArray[0] = myConstExprFunction(10);
myArray[1] = myConstExprFunction(20);
// ...
return myArray;
}
Then, initialize your array:
constexpr auto myArray = makeMyArray();
constexpr declares that it is possible to evaluate the value of the function or variable at compile time.
So the only way you could use it with array is like:
constexpr int myArray[100]{1 , 2 , 3 ,.........};
statements like
myArray[0] = myConstexprFunction(0);
can only be evaluated during runtime. So its not possible.
If you want to declare constexpr an array and initialize it's value using a constexpr function... the best I can think is wrap the array in a struct/array and initialize it via a delegate constructor.
The following is a full working C++14 example
#include <utility>
#include <iostream>
constexpr int myConstexprFunction (int i)
{ return i << 1; } // return 2*i
template <std::size_t S>
struct wrapArray
{
int const myWrappedArray[S];
template <int ... Is>
constexpr wrapArray (std::integer_sequence<int, Is...> const &)
: myWrappedArray { myConstexprFunction(Is)... }
{ }
constexpr wrapArray ()
: wrapArray(std::make_integer_sequence<int, S>())
{ }
};
int main ()
{
constexpr wrapArray<100> wa100;
for ( auto i : wa100.myWrappedArray )
std::cout << i << ", ";
std::cout << std::endl;
}
If you need a C++11 code, you have to implement a substitute for std::integer_sequence and for std::make_integer_sequence(). It's not difficult.
No.
constexpr variables must be "immediately initialised".
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.