6

I am initialising a very large array with thousands of set values. I would like these values set at compile time rather than run time as they are fixed and will not change.

Is there a way of generating these values automatically, perhaps using the preprocessor? Currently I am generating these values using another small program then simply copying and pasting the values in respectively.

Here is what I am generating:

class MyClass
{
public:
    MyClass(int x, int y, int z) : X(x), Y(y), Z(z) {}

    int X, Y, Z;
};

std::vector<MyClass> my_vector{
    #include "my_vector_default_values.h"
};

my_vector_default_values.h

MyClass(0, 0, 1),
MyClass(0, 0, 2),
MyClass(0, 0, 3),
MyClass(0, 0, 4),
// etc... for thousands of lines
// ...

Edit:

The actual values I am generating are generated as follows (this is the C# program):

var sb = new StringBuilder();

var sizeX = 32;
var sizeY = 32;
var sizeZ = 32;

for (var x = 0; x < sizeX; x++)
{
    for (var y = 0; y < sizeY; y++)
    {
        for (var z = 0; z < sizeZ; z++)
        {
            sb.AppendLine($"MyClass({x}, {y}, {z}),");
        }
    }
}

var s = sb.ToString();
rhughes
  • 9,257
  • 11
  • 59
  • 87
  • That's an intelligent use of `#include` that I hadn't thought of before. – byxor Jul 05 '19 at 03:27
  • 3
    Forget about the preprocessor. In C++, you're better off doing this with a constexpr initializer. Obviously, the complexity depends on the complexity of the computation. OTOH, I don't see anything wrong with you current strategy, if it works, but I don't see why you don't just write directly to the file you will #include. That way, you can automate the file generation as part of your build procedure and avoid intervening with a copy-and-paste. – rici Jul 05 '19 at 03:29
  • @rici I would be interested in a `constexpr` solution as well if it would achieve the same result. The generated values are very simple to compute. – rhughes Jul 05 '19 at 03:31
  • 1
    If performance is a concern, you might get _extremely small_ speedups if you remove all of the new-line characters from `my_vector_default_values.h`. – byxor Jul 05 '19 at 03:31
  • 1
    "Is there a way of generating these values automatically" if you tell us which way you generate them we may say something. Otherwise it requires telepathy machine which does not work today. – Slava Jul 05 '19 at 03:33
  • Here's a very related question and answer, then: https://stackoverflow.com/questions/47285082/modern-c-initialize-constexpr-tables That's just one of many examples I found with a search on "constexpr lookup tables". – rici Jul 05 '19 at 03:36
  • @rici are you suggesting initializing a constexpr array and then copying to a vector ? – M.M Jul 05 '19 at 03:38
  • 1
    the edit is confusing, the original example asks about `int` but then there is some kind of class invocation in the edit (that makes a big difference to the solution) – M.M Jul 05 '19 at 03:39
  • @MM: only if the table is going to be mutated during program execution. – rici Jul 05 '19 at 03:40
  • 1
    OK it'd be good if OP could clarify that (the term "default values" normally implies that other values could be set at runtime, and the choice of `vector` suggests they wanted runtime sizing) – M.M Jul 05 '19 at 03:41
  • If they are just *even* values starting with `2`, that seems somewhat trivial to generate on the fly... – David C. Rankin Jul 05 '19 at 03:43
  • @M.M I have updated my question. – rhughes Jul 05 '19 at 03:47
  • Are you amenable to removing the constructor (or adding a default constructor), it would make the code a bit simpler. For the non-default-constructible version see [this answer](https://stackoverflow.com/a/37899840/1505939) for a code skeleton – M.M Jul 05 '19 at 03:54
  • @M.M I'm happy to remove the constructor. – rhughes Jul 05 '19 at 04:02
  • 1
    FYI, it's _possible_ to do this easily with boost preprocessor, but for 32*32*32 entries, preprocessing just the solution takes over half a minute on my admittedly not too high powered machine. – H Walters Jul 05 '19 at 04:24

1 Answers1

4

Here is some sample C++17 code, if you can make the class default-constructible:

#include <array>

const size_t gX = 32, gY = 32, gZ = 32;

class MyClass
{
public:
    int X, Y, Z;
};

constexpr std::array<MyClass, gX*gY*gZ> gen()
{
    std::array<MyClass, gX*gY*gZ> r{};
    size_t n = 0;

    for (int x = 0; x < gX; ++x)
        for (int y = 0; y < gY; ++y)
            for (int z = 0; z < gZ; ++z)
                r[n++] = { x, y, z };

    return r;
}

extern constexpr auto global = gen();

int main()
{
    return global[35].Z;
}

As you can see from the assembly output, the table was computed at compile-time.


Since C++17 std::array allows operator[] to be used in a constexpr manner. Prior to C++17, or if you want to retain the class's non-default-constructibility, you'd have to use variadic templates to generate a braced initializer for the array; this answer provides a code outline that you could modify to use your generation algorithm.

M.M
  • 138,810
  • 21
  • 208
  • 365