Search code examples
c++arraysconstexprlist-initialization

Brace initialization of a member array with another constexpr array


Let's consider following illustrative example code:

using matrix_t = double[2][2];

constexpr matrix_t zero_matrix = {
    {0.0, 0.0},
    {0.0, 0.0}
};

constexpr matrix_t identity_matrix = {
    {1.0, 0.0},
    {0.0, 1.0}
};

struct wrapped_matrix_t
{
    matrix_t matrix;
};

constexpr wrapped_matrix_t zero_wrapped_matrix = {
    //zero_matrix
    {
        {zero_matrix[0][0], zero_matrix[0][1]},
        {zero_matrix[1][0], zero_matrix[1][1]}
    }
};

constexpr wrapped_matrix_t identity_wrapped_matrix = {
    //identity_matrix
    {
        {identity_matrix[0][0], identity_matrix[0][1]},
        {identity_matrix[1][0], identity_matrix[1][1]}
    }
};

Now I would like to be able to use by constexpr arrays zero_matrix and identity_matrix to initialize matrix_t members in other types. However that seems to not be possible. Or at least simple use of the name doesn't work.

The best what I come out with is reuse of values be referring to indexes. But this is far from perfect.

Is there any way to use zero_matrix and identity_matrix directly in such initializations?

(I'm checking on GCC 6.3.0 with -Wall -Wextra -pedantic -std=c++11.)


Solution

  • If you cannot use std::array and don't have access to the implementation of wrapped_matrix_t, you can use metaprogramming to generate the indexing of the source array when initializing your wrappers. The final code will look like this:

    constexpr wrapped_matrix_t zero_wrapped_matrix = from_array(zero_matrix);
    constexpr wrapped_matrix_t identity_wrapped_matrix = from_array(identity_matrix);
    

    I'm assuming that matrix_t is double[2 * 2], but the technique below can be generalized to N-dimension arrays.

    Here are the implementation details:

    template <typename Array, std::size_t... Is>
    constexpr auto from_array_impl(const Array& arr, std::index_sequence<Is...>)
    {
        return wrapped_matrix_t{arr[Is]...};
    }
    
    template <std::size_t N>
    constexpr auto from_array(const double(&arr)[N])
    {
        return from_array_impl(arr, std::make_index_sequence<N>());
    }
    

    I'm basically matching the size of the array with a reference, and building a 0..N index sequence from it. I'm then creating a wrapped_matrix_t by indexing the array with the sequence.

    live example on wandbox


    Here's a possible implementation for 2D arrays:

    template <std::size_t W, typename Array, std::size_t... Is>
    constexpr auto from_array_impl(const Array& arr, std::index_sequence<Is...>)
    {
        return wrapped_matrix_t{(arr[Is % W][Is / W])...};
    }
    
    template <std::size_t W, std::size_t H>
    constexpr auto from_array(const double(&arr)[W][H])
    {
        return from_array_impl<W>(arr, std::make_index_sequence<W * H>());
    }
    

    live example on wandbox