lvella
lvella

Reputation: 13443

Can you dynamically generate an initializer list?

I have a class MyClass with no default constructor, and I want to create a std::array<MyClass, 8>. I believe I can initialize my array like this:

std::array<MyClass, 8> my_array = {
    {arg00, arg01, /* ... */, arg0N},
    {arg10, arg11, /* ... */, arg1N},
    /* ... */
    {arg70, arg71, /* ... */, arg7N}
};

where argij is the j-nth argument (out of N) of the i-nth element's constructor (out of 8). Now, spelling out these arguments is a boring and error prone business, and it is much more clear and straightforward to automatically generate/assemble them, which can be done as something like this:

for(uint8_t a = 0; a < 2; ++a) {
    for(uint8_t b = 0; b < 2; ++b) {
        for(uint8_t c = 0; c < 2; ++c) {
            std::tie(arg0, arg1, /* ... */, argN) = generate_args(a, b, c);

            /* TODO: do something with arg0, arg1 ... argN */
        }
    }
}

So, the question: is there a way to create my std::array<MyClass, 8> my_array; from the dynamically generated constructor arguments?

EDIT:

Currently I have a default constructor on MyClass, so I can initialize my_array[counter++] = MyClass(args...); from inside the loops. But I don't like allowing MyClass to be created in an uninitialized state.

Upvotes: 1

Views: 499

Answers (1)

Toby Speight
Toby Speight

Reputation: 30831

No, you can't work with initializer lists like that. But you might be able to borrow a trick from std::experimental::to_array() and use a std::index_sequence template to convert a suitable vector:

template <std::size_t... I>
static constexpr std::array<MyClass, 8>
to_array(std::vector<MyClass>&& v, std::index_sequence<I...>)
{
    return { {v[I]...} };
}


const std::array<MyClass, 8> my_array =
    []{
        std::vector<MyClass> v;
        for(uint8_t a = 0; a < 2; ++a) {
            for(uint8_t b = 0; b < 2; ++b) {
                for(uint8_t c = 0; c < 2; ++c) {
                    int arg0, arg1, arg2, arg3;
                    std::tie(arg0, arg1, arg2, arg3) = generate_args(a, b, c);
                    v.emplace_back(arg0, arg1, arg2, arg3);
                }
            }
        }
        return to_array(std::move(v), std::make_index_sequence<8>{});
    }();

If your MyClass isn't copyable or movable, perhaps directly construct into the array with a similar index sequence template (but generate a, b and c back from the index):

template <std::size_t... I>
constexpr std::array<MyClass, 8>
generate(std::index_sequence<I...>)
{
    auto create =
        [](std::size_t i) {
            const auto& [arg0, arg1, arg2, arg3]
                = generate_args((i>>2)&1, (i>>1)&1, (i>>0)&1);
            return MyClass{arg0, arg1, arg2, arg3};
        };
    return { create(I)... };
}


const std::array<MyClass, 8> my_array = generate(std::make_index_sequence<8>{});

Upvotes: 4

Related Questions