Create N-Element Constexpr Array in C++11

Create N-element constexpr array in C++11

In C++14 it can be easily done with a constexpr constructor and a loop:

#include <iostream>

template<int N>
struct A {
constexpr A() : arr() {
for (auto i = 0; i != N; ++i)
arr[i] = i;
}
int arr[N];
};

int main() {
constexpr auto a = A<4>();
for (auto x : a.arr)
std::cout << x << '\n';
}

c++11 way of returning an array of unknown number of elements

Trailing return type should do the job:

struct DataBinding {
static constexpr auto getRawBindings()
-> decltype(
asConstArray(
DEF_BINDING(int, stateProp, stateParam), //BindingInfo constexpr object
DEF_BINDING(float, areaProp, areaParam) //BindingInfo constexpr object
//(...)
)
)
{
return asConstArray(
DEF_BINDING(int, stateProp, stateParam), //BindingInfo constexpr object
DEF_BINDING(float, areaProp, areaParam) //BindingInfo constexpr object
//(...)
);
}
};

To avoid the repetition, MACRO might help:

#define RETURN(Expr) decltype(Expr) { return Expr; }

and then

struct DataBinding {
static constexpr auto getRawBindings()
-> RETURN(
asConstArray(
DEF_BINDING(int, stateProp, stateParam), //BindingInfo constexpr object
DEF_BINDING(float, areaProp, areaParam) //BindingInfo constexpr object
//(...)
)
)
};

How to generate an inline constexpr plain array of array in c++17

Just use std::array<std::span<int>, N>, which is a fixed size array of spans of different sizes. To generate this, use an std::index_sequence

Header:

constexpr std::size_t size_of_A = 500;
extern const std::array<const std::span<const int>, size_of_A>& A;

Implementation:

constexpr std::size_t size_of_B_in_A(std::size_t i) { return i%10+1;}
constexpr int f(std::size_t i, std::size_t j) {return static_cast<int>(i%(j+1));}

template <int I, int N>
struct B
{
std::array<int,N> arr;
explicit constexpr B()
{
for (int j = 0; j < N; j++)
arr[j] = f(I, j);
}
constexpr operator const std::span<const int>() const {return {arr};}
};

template<class index_sequence>
class BGen;
template<std::size_t... I>
struct BGen<std::integer_sequence<std::size_t,I...>> {
static constexpr std::tuple<B<I, size_of_B_in_A(I)>...> bs{};
static constexpr std::array<const std::span<const int>, sizeof...(I)> A {std::get<I>(bs)...};
};
const std::array<const std::span<const int>, size_of_A>& A
= BGen<decltype(std::make_index_sequence<size_of_A>{})>::A;

Usage:

int main()
{
for (unsigned i = 0; i < A.size() ; i++)
{
for (unsigned j = 0; j < A[i].size(); j++)
{
std::cout << A[i][j];
}
}
}

http://coliru.stacked-crooked.com/a/d68b0e9fd6142f86


However, stepping back: This solution is NOT the normal way to go about solving this problem. Since it's all constexpr, this is all data not code. Ergo, the most performant solution is two programs. One generates the data and saves it to a file that ships with (inside?) your program. Then your program simply maps the file into memory, and uses the data directly.

Populate An Array Using Constexpr at Compile-time

Ignoring ALL the issues, indices to the rescue:

template<unsigned... Is> struct seq{};
template<unsigned N, unsigned... Is>
struct gen_seq : gen_seq<N-1, N-1, Is...>{};
template<unsigned... Is>
struct gen_seq<0, Is...> : seq<Is...>{};

template<unsigned... Is>
constexpr Table MagicFunction(seq<Is...>){
return {{ whichCategory(Is)... }};
}

constexpr Table MagicFunction(){
return MagicFunction(gen_seq<128>{});
}

Live example.

populating a constexpr array using C++17

This is a restriction in C++ until C++20, see Permitting trivial default initialization in constexpr contexts: You have to initialize all variables inside of a constexpr function. The Standard gives the following example:

C++17

constexpr int uninit() {
int a; // error: variable is uninitialized
return a;
}

C++20

constexpr int uninit() {
struct { int a; } s;
return s.a; // error: uninitialized read of s.a
}

Note that the initialization itself is OK in C++20. Reading the indeterminate value (inside the "uninitialized" object) is not OK.


To fix your issue, initialize the array or switch to C++20:

static constexpr std::array<int, N> getArr()
{
std::array<int, N> a{}; // now initialized!
for (auto idx = 0; idx < a.size(); ++idx)
{
a[idx] = idx * idx;
}
return a;
}

constexpr array initialisation

Like said here

Because std::array<T, N> is an aggregate, it can be initialized as a constexpr if and only if the underlying type T has a constexpr constructor (when presented with each initializer you provide).

As std::vector doesn't have a constexpr constructor (yet), this will not work.

So with pre-C++20 this will not work with dynamic size STL containers. No solution or quick fix. Don't get your hopes up.

The alternative is to design your own constexpr fixed-max-size Vector class. e.g.

template <typename T, std::size_t N>
class Vector {
private:
T values[N]{};
public:
std::size_t size{ 0 };
constexpr bool empty() const noexcept { return size == 0; }
constexpr void clear() noexcept { size = 0; }

constexpr T* begin() noexcept { return &values[0]; }
constexpr T* end() noexcept { return &values[size]; }
constexpr T const* cbegin() const noexcept { return &values[0]; }
constexpr T const* cend() const noexcept { return &values[size]; }

constexpr T& back() noexcept { return values[size - 1]; }

constexpr T operator[] (int const loc) const noexcept { return values[loc]; }
constexpr T& operator[] (int const loc) noexcept { return values[loc]; }

constexpr void push_back(T const& value) noexcept { values[size++] = value; }

constexpr void resize(int const newSize) noexcept {
auto const diff = newSize - size;
if (diff > 0) memset(end(), 0, diff * sizeof(T));
size = newSize;
}
};

This is one I use sometimes... you need to add a initializer_list constructor.

edit: Quick test... this seems to compile.

#include <array>

template <typename T, std::size_t N>
class Vector {
private:
T values[N]{};
public:
std::size_t size{ 0 };
constexpr Vector(std::initializer_list<T> il) noexcept
: size(std::distance(std::cbegin(il), std::cend(il)))
{
std::size_t i = 0;
for(auto it = std::cbegin(il); it != std::cend(il); ++it) {
values[i++]=*it;
}
}
};

struct SomeStruct {
Vector<int,5> vec;
};

int main() {
[[maybe_unused]]constexpr std::array<SomeStruct, 2> arr {
SomeStruct {
.vec = {
1, 2
}
},
SomeStruct {
.vec = {
3, 4, 5
}
}
};
}

Using `constexpr` with array initialization

Static constexpr data member declarations aren't definitions in C++11/14, therefore you cannot odr-use prim.

To work around it, put the following statement somewhere in your cpp file as you would do with any other non-constexpr static data member:

constexpr const float bar::prim[4];

In other terms, this returns an undefined reference:

struct bar {
static constexpr const float prim[4] = {2, 3, 5, 7};
};

int main() {
auto *foo = bar::prim;
}

This doesn't:

struct bar {
static constexpr const float prim[4] = {2, 3, 5, 7};
};

constexpr const float bar::prim[4];

int main() {
auto *foo = bar::prim;
}

Because in the second case you are actually defining prim other than declaring it and thus you can get its address, use it by means of a reference, and so on...

Is it possible to initialize constexpr std::array member in a programtic way

You don't need anything special, constexpr function requirements are very relaxed these days:

#include <iostream>
#include <array>

template <int N> constexpr std::array<int, N> first_n_fibs()
{
std::array<int, N> ret{};
ret[0] = 0;
if (N == 1) return ret;
ret[1] = 1;
for (int i = 2; i < N; i++)
ret[i] = ret[i-2] + ret[i-1];
return ret;
}

int main()
{
constexpr auto a = first_n_fibs<3>();
}

(try it live)


std::array constructors are not constexpr

Apparently it has no user-defined constructors at all, so nothing stops its construction from being constexpr.



Related Topics



Leave a reply



Submit