Pregunta

Brief description:

Consider a variadic-template based typelist used to hold integral values:

template<typename... Ts>
struct list {};

using my_int_list = list<std::integral_constant<0>,
                         std::integral_constant<1>,
                         std::integral_constant<2>>;

This could be dumped to an array using array initializers and variadic pack expansion:

template<typename LIST>
struct to_array;

template<typename... Ts>
struct to_array<list<Ts...>>
{
    static constexpr unsigned int result[] = { Ts::value... };
}; 

Now consider I want to do the same thing with 2d arrays (In other words, the input is a typelist of typelists). We could use the later metafunction to dump subarrays, and a second metafunction to dump the outer array:

template<typename LIST>
struct to_2d_array;

template<typename... Ts>
struct to_2d_array<list<Ts...>>
{
    using value_type = unsigned int; //To simplify things, suppose we know the type
                                     //of the elements. Also suppose the array is 
                                     //squared.

    static constexpr value_type result[sizeof...(Ts)][sizeof...(Ts)] = { to_array<Ts>::result... };
};

My problem (i.e. context in depth):

I'm writting a compile-time Mandelbrot fractal render. The render works "fine"1, and it returns the result as a square 2d typelist (Typelist of typelists of the same length) of RGB values. The to_2d_array metafunction is needed to dump the result to an array and write it in a PPM file at runtime.

The RGB values are instances of an integral wrapper equivalent to std::integral_constant<unsigned int>, it has a member value which holds the value.

The code I posted above is exactly what I have written in my implementation, using standard types (std::integral_constant) instead of my own types. The code above works perfectly at coliru, but my compiler (GCC4.8.1) says:

The initializer needs to be enclosed with an additional encloser-brace.

in to_2d_array. If I put the extra braces, the assigment compilation fails with a "invalid cast from pointer to array".

What I'm doing wrong? Is there another approximation to achieve this?

[1] Really it isn't working now, because the compilation of this template-metaprogramming monster leads to a GCC internal segmentation fault :). But this problem is not related to the question...

¿Fue útil?

Solución

According to what's written in your coliru example, There are a few problems I'd like to point out.

  1. The types of result.

    The following code does not compile.

    int main() {
      int x[] = {1, 2, 3};
      int y[3][3] = {x, x, x};
    }
    

    whereas the following does.

    #include <array>
    
    int main() {
      std::array<int, 3> x = {1, 2, 3};
      std::array<std::array<int, 3>, 3> y = {x, x, x};
    }
    

    The types of result in to_array<> and to_2d_array<> is equivalent to the first example.

  2. The result is declared as static constexpr, but the out-of-line definition is missing.

The following is the code with modifications made to fix the issues addressed above.

#include <array>
#include <iostream>
#include <type_traits>

template <typename... Ts>
struct list {};

template <typename List>
struct to_array;

template <typename... Ts>
struct to_array<list<Ts...>> {

    using result_type = std::array<unsigned int, sizeof...(Ts)>;

    /* Use std::array<> instead of C-array. */
    static constexpr result_type result = { Ts::value... };

};  // ToArray<List<Ts...>>

/* Out-of-line definition for static constexpr variable. */    
template <typename... Ts>
constexpr typename to_array<list<Ts...>>::result_type
    to_array<list<Ts...>>::result;

template <typename List>
struct to_2d_array;

template <typename... Ts>
struct to_2d_array<list<Ts...>> {

    using result_type = 
        std::array<std::array<unsigned int, sizeof...(Ts)>, sizeof...(Ts)>;

    /* Use std::array<> instead of C-array. */
    static constexpr result_type result = { to_array<Ts>::result... };
};

/* Out-of-line definition for static constexpr variable. */
template <typename... Ts>
constexpr typename to_2d_array<list<Ts...>>::result_type 
    to_2d_array<list<Ts...>>::result;

int main() {
  using my_int_list = list<std::integral_constant<int, 0>,
                           std::integral_constant<int, 1>,
                           std::integral_constant<int, 2>>;
  for (int i = 0; i < 3; ++i) {
    std::cout << to_array<my_int_list>::result[i] << ' ';
  }  // for
  std::cout << std::endl << std::endl;
  using my_2d_list = list<my_int_list,my_int_list,my_int_list>;
  /* Actually try printing the result. */
  for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 3; ++j) {
      std::cout << to_2d_array<my_2d_list>::result[i][j] << ' ';
    }  // for
    std::cout << std::endl;
  }  // for
}

Prints:

0 1 2

0 1 2
0 1 2
0 1 2

Tested with gcc 4.8.2, clang 3.3 and whichever gcc 4.8 is on Coliru.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top