3

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]

selamba
  • 384
  • 4
  • 12
  • 3
    The `std::iota` function will initialize your vector with incrementing values. But why do you need such a vector? (Accessing any element, `v[i]` will just give you `i`.) – Adrian Mole Nov 07 '21 at 22:15
  • @AdrianMole but we can take elements out and put them back in, can we? It is to keep track of what numbers are available. My other question where I describe the use case: https://stackoverflow.com/questions/69872375/c-stl-stack-vs-forward-list/ – selamba Nov 07 '21 at 22:21

2 Answers2

5

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}
sehe
  • 374,641
  • 47
  • 450
  • 633
0

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();
};
gexicide
  • 38,535
  • 21
  • 92
  • 152
  • I have just considered it a few minutes ago. I don't like that the make_vector function is then publicly available to all who include the file. One solution would be to actually implement the goddamn modules that have been in the standard since c++20. – selamba Nov 08 '21 at 11:54
  • @selamba: pretty simple, put the function into the class and make it private. – gexicide Nov 08 '21 at 14:17
  • If you don't like that it is even visible in the header, then don't make the variable `inline` and instead initialize it in the `.cpp` file and nobody will ever see that initializer function. – gexicide Nov 08 '21 at 14:26