1

I have an array I want to initialise as a constexpr based on template paramaters (I think this will require c++14, as I envisage the answer requiring initialiser lists as constexpr).

Lets say I have a template

template<T t>

where

 T = int[1][2][3]

now, i can extract the array sizes recursively using type_traits std::extent

what I'd like to do ultimately is generate a constexpr member with the dimensions of T as elements of myarray

std::array<int,3> myarray = {1,2,3};

I've seen what looks like a nice way to initialise an array using a variadic template ref: How to construct std::array object with initializer list?

The question is, how to generate either an initialiser list or variadic template with the dimensions of T given T?1

Community
  • 1
  • 1
OllieB
  • 1,431
  • 9
  • 14
  • 1
    @R.MartinhoFernandes: I prefer to engage my brain before typing stuff in and hoping it will compile. And as I can't navigate the problem, I a) read through a lot of the internet, including stackoverflow then b), posted a clear question stating my problem as a minimum example. Is this not sufficant? – OllieB Oct 31 '13 at 08:43

2 Answers2

2

The following is a bit complicated but it should work (with C++11):

#include <array>
#include <type_traits>

template<std::size_t...> struct seq {};

template<typename,typename> struct cat;
template<std::size_t... Is, std::size_t... Js>
struct cat<seq<Is...>,seq<Js...>>
{
    using type = seq<Is...,Js...>;
};

template<typename> struct extract_seq { using type = seq<>; };
template<typename T,std::size_t N>
struct extract_seq<T[N]>
{
    using type = typename cat< seq<N>, typename extract_seq<T>::type >::type;
};

template<typename T> struct extract_type { using type = T; };
template<typename T,std::size_t N>
struct extract_type<T[N]>
  : extract_type<T>
{};

template<typename,typename> struct to_array_helper;
template<typename T, std::size_t... Is>
struct to_array_helper<T,seq<Is...>>
{
    constexpr static const std::array<T,sizeof...(Is)> value {{ Is... }};
};

template<typename T, std::size_t... Is>
constexpr const std::array<T,sizeof...(Is)>
to_array_helper<T,seq<Is...>>::value;

template<typename T>
struct to_array
  : to_array_helper<typename extract_type<T>::type,
                    typename extract_seq<T>::type>
{};

int main()
{
    auto arr = to_array< int[1][2][3] >::value;
}

Live example

Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
  • 1
    I'm not sure if I completely grasped the question (tired), but isn't something [like this](http://coliru.stacked-crooked.com/a/a869476c55fe5a41) a bit simpler? – dyp Oct 31 '13 at 00:22
  • 1
    (Or [that](http://coliru.stacked-crooked.com/a/89543810a4c99973), might be a bit more flexible) – dyp Oct 31 '13 at 00:29
  • 1
    @DyP Nice, your version is much easier although you repeated the `int` in `to_array`. But that can be nicely integrated, see [here](http://coliru.stacked-crooked.com/a/a137ebe204ff314a). – Daniel Frey Oct 31 '13 at 00:29
  • 1
    Well that was partially intentional. I had something like the solution in the second comment in mind, where you can specify the return type; specifying the element type of the array somehow was a first simple step. – dyp Oct 31 '13 at 00:33
  • 1
    @DyP I see, you were going for a more general solution while I was trying to concentrate on what the OP needs. Anyways, he should have plenty of code to play with now. :) – Daniel Frey Oct 31 '13 at 00:35
1

I don't think you need any special future C++. This works fine in C++11:

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

#include <prettyprint.hpp>

template <typename T>
struct Foo
{
    std::array<std::size_t, std::rank<T>::value> a;

    Foo() : Foo(X<std::rank<T>::value>(), Y<>(), Z<T>()) { }

private:
    template <unsigned int K>  struct X { };
    template <unsigned int ...I> struct Y { };
    template <typename> struct Z { };

    template <typename U, unsigned int K, unsigned int ...I>
    Foo(X<K>, Y<I...>, Z<U>)
    : Foo(X<K - 1>(),
          Y<I..., std::extent<U>::value>(),
          Z<typename std::remove_extent<U>::type>())
    { }

    template <typename U, unsigned int ...I>
    Foo(X<0>, Y<I...>, Z<U>) 
    : a { I... }
    { }
};

int main()
{
    Foo<char[4][9][1]> x;
    std::cout << x.a << std::endl;
}

Outputs:

[4, 9, 1]
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • This numerates the dimensions, but doesn't return the dimensions themselves. For instance consider T = int[7][14][13]. The desired result array contains 7 14 13 – OllieB Oct 31 '13 at 08:49
  • @OllieB: Oh, I see, I got thrown off by your example. Hm, maybe sticking `std::extent` somewhere in there fixes that. Let me think about it. – Kerrek SB Oct 31 '13 at 08:51
  • 1
    @OllieB: There, fixed. – Kerrek SB Oct 31 '13 at 08:56