Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to set the value of elements in a constexpr array after declaration?

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

like image 962
user3770392 Avatar asked Apr 04 '17 11:04

user3770392


4 Answers

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:

  1. constexpr lambda
  2. 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();
like image 58
kennytm Avatar answered Oct 26 '22 23:10

kennytm


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();
like image 28
Guillaume Racicot Avatar answered Oct 27 '22 01:10

Guillaume Racicot


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.

like image 37
Sumeet Avatar answered Oct 27 '22 01:10

Sumeet


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.

like image 25
max66 Avatar answered Oct 27 '22 00:10

max66