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).
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 struggling with the creation of an object, because i have to create a new parameter pack from an array inside a function. The array contains a few enum elements of the same enum type.
The error message states, that the array is not static, thus not a valid input that could be converted into a parameter pack.
Is there a possibility to something as follows:
enum Enums{e1, e2};
template<typename T, auto ...Enums>
class Example{};
template<int size>
constexpr auto createObj(){
Enums enum_array[size] = {};
for(int i = 0; i< size ; i++){
//fill array with e1 and e2
}
return Example<double, enum_array>();
}
Error message:
error: the address of ‘enum_array’ is not a valid template argument because it does not have static storage duration
return Example<double, enum_array>();
You'll probably need a few indirection level. First, to use any value as template parameter, it must be a compile time constant for the compiler to instanciate the template and generate its code.
For that, you'll need to put the function calculating the array in a constexpr function:
template<int size>
constexpr auto createObjArray() {
auto enum_array = std::array<Enums, size>{};
for(int i = 0; i < size ; i++){
//fill array with e1 and e2
}
return enum_array;
}
Then, you'll need a way to expand the array into a pack. Right now in C++17 (and C++20) it cannot be done locally. You'll need an index sequence of some sort. The most straightforward way to do that is to use an helper function:
template<std::size_t... S>
constexpr auto createObjHelper(std::index_sequence<S...>){
constexpr auto enumArray = createObjArray<sizeof...(S)>();
return Example<double, enumArray[S]...>();
}
Then wrap all that from a single user facing function:
template<int size>
constexpr auto createObj(){
return createObjHelper(std::make_index_sequence<size>());
}
Note that many of those helper function can be achieved using lambdas. For example, a lambda can have a constexpr body and be executed at compile time inside a runtime function:
template<std::size_t... S>
constexpr auto createObjHelper(std::index_sequence<S...>){
// implicitly constexpr ---v
constexpr auto enumArray = []{
auto enum_array = std::array<Enums, size>{};
for(int i = 0; i < size ; i++){
//fill array with e1 and e2
}
return enum_array;
}();
return Example<double, enumArray[S]...>();
}
And In C++20, you can use familiar template function syntax for lambda to avoid the helper function:
template<int size>
constexpr auto createObj(){
// implicitly constexpr ---v
constexpr auto enumArray = []{
Enums enum_array[size] = {};
for(int i = 0; i < size ; i++){
//fill array with e1 and e2
}
return enum_array;
}();
return []<std::size_t... S>(std::index_sequence<S...>) -> decltype(auto) {
return Example<double, enumArray[S]...>();
}(std::make_index_sequence<size>());
}
In C++23, if structured binding pack introduction is approved, then the second lambda can be removed in favor of creating a new pack directly in the function:
template<int size>
constexpr auto createObj(){
// implicitly constexpr ---v
constexpr auto enumArray = []{
Enums enum_array[size] = {};
for(int i = 0; i < size ; i++){
//fill array with e1 and e2
}
return enum_array;
}();
constexpr auto&& [...allEnums] = enumArray;
return Example<double, allEnums...>();
}
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>{});
}
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".
I can't use constexpr value in function, as opposed to outside of the function.
I can use auto ar1 = std::array<int, il.size()>(); in scope where il is defined.
But I can't use { return std::array<T, il.size()>();} in constexpr-function il_to_array()
Why can not I use constexpr value in function, but I can do the same in block scope of this value?
http://ideone.com/5g0iRE
#include <iostream>
#include <initializer_list>
#include <array>
constexpr size_t size_to_size(size_t v) { return v; } // 1 - OK
template<typename T>
constexpr size_t il_to_size(std::initializer_list<T> il) { return il.size(); } // 2 - OK
// 3 - error
template<typename T>
constexpr auto il_to_array(std::initializer_list<T> il) {return std::array<T, il.size()>();}
template<size_t N>
void print_constexpr() { std::cout << N << std::endl; }
int main() {
constexpr std::initializer_list<int> il = { 1, 2, 3 };
print_constexpr<il.size()>(); // 0 - OK
print_constexpr< size_to_size(il.size()) >(); // 1 - OK
print_constexpr< il_to_size(il) >(); // 2 - OK
auto ar1 = std::array<int, il.size()>(); // OK - body of function: il_to_array()
//auto ar2 = il_to_array(il); // 3 - error
return 0;
}
For example, there we see, that template-constexpr-function will not fail, even if it may be or may not be constexpr - depends of T, because one of instance may be constexpr: Why does the C++ compiler makes it possible to declare a function as constexpr, which can not be constexpr?
And it can be concluded that if this is a template-function, it may be specialization any of: constexpr and non-constexpr.
And on the basis of SFINAE - if we use only constexpr-arguments then instantiates only constexpr-instance, and it does not matter that non-constexpr-function could not be instantiated.
The arguments of (constexpr) functions are not constexpr.
constexpr functions might be given arguments which were not known at compile time.
So following is valid whether v is known at compile time or not
constexpr size_t size_to_size(size_t v) { return v; }
But following function doesn't work as il is not constexpr and non type template parameter requires to be known at compile time:
template<typename T>
constexpr auto il_to_array(std::initializer_list<T> il) {return std::array<T, il.size()>();}
This is true even if the function is only called with arguments known at compile time.