C++ initialize an STL container using logic, not hand-writing data

Viewed 80

I have a static inline member of a class - an std::vector. I need to fill it with numbers - 0 through to std::numeric_limits<uint16_t>::max() - 1.

I know there is list-initialization for containers, but I don't feel like coming up with a macro/struct that will expand to 1,2,3, all the way to 65534.

I want to know if there is a way to cleanly initialize the container with such a sequence of numbers, considering it's a static inline variable.

The container needs to be static, but if there is a way to do what I want that requires it to not be inline, then so be it.

One way is to write a function that will fill the container and then use #pragma startup myfunc or [[gnu::constructor]], but neither the macro nor the attribute are in the actual standard. And I don't want to have functions like init_mylibrary() akin to glfwInit() that the user has to call in main() before using mylibrary.

An approach that will possibly work is to declare the container's size through the constructor and supply a custom allocator that will initialize the memory with consecutive integers, but writing an entire allocator for this task seems like overkill. There has to be a way to do this cleanly, like how Ruby allows one to write Array.new(4) { |i| i + 10 } #=> [10, 11, 12, 13]

2 Answers

You can do this with std::generate_n, std::iota, but not during vector construction.

RangeV3 can do a similar thing though:

Compiler Explorer

#include <range/v3/all.hpp>
using ranges::views::iota;

struct X {
    std::vector<int> v;

    X(int n) : v(ranges::to_vector(iota(0, n))) {}
};

#include <fmt/ranges.h>
int main()
{
    X x(42);
    fmt::print("{}\n", x.v);
}

Prints

{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41}

There is no need for fancy construction concepts, even though they do exist (see other answer).

You can just write a function that creates and fills a vector with whatever you want (be it 0..n or some way more complicated logic), then have that function return the vector and use the function to initialize your static inline variable:

std::vector<int> make_vector() {
    ... // Your potentially super-complicated logic here
} 

struct foo{
    static inline std::vector<int> bar = make_vector();
};
Related