I want to declare template:
template <size_t N, class Type> my_tuple
{
using type = ... //something here
};
So that this my_typle<3, std::string>::type,for example, will be the same as this std::tuple<std::string, std::string, std::string>
Please, show, how it can be done in one line, maybe, using std::index_sequence or something from boost or whatever? Or maybe it can not be done just simple?
UPD
Please, note I do not need an std::array, I need to parametrize some variable template with predefined list of types. I have used std::tuple as an example here.
This is fun. Here's a "pure" meta-programming approach to expand the sequence:
template<typename T, typename Seq>
struct expander;
template<typename T, std::size_t... Is>
struct expander<T, std::index_sequence<Is...>> {
template<typename E, std::size_t>
using elem = E;
using type = std::tuple<elem<T, Is>...>;
};
template <size_t N, class Type>
struct my_tuple
{
using type = typename expander<Type, std::make_index_sequence<N>>::type;
};
I say "pure" ironically. It's more akin to the classic meta-programming tricks, nothing else.
Use decltype with return type of function returning tuple<string,string,...repeated N times>:
template<typename T, size_t ... Indices>
auto GetType(std::index_sequence<Indices...>) {
return std::make_tuple( (Indices,T{})... );
}
template <size_t N, class Type> class my_tuple
{
using type = decltype(GetType<Type>(std::make_index_sequence<N>())); //something here
};
template<class T>
struct Dummy;
int main(){
Dummy<my_tuple<3,std::string>::type> d;
error on d gives you tuple<string,string,string>, so it is your desired type.
You can
template <typename...>
struct cat_tuple_type;
template <typename... T1, typename... T2>
struct cat_tuple_type<std::tuple<T1...>, std::tuple<T2...>>
{
using type = std::tuple<T1..., T2...>;
};
template <size_t N, class Type> struct my_tuple
{
static_assert(N>0);
using type = typename cat_tuple_type<std::tuple<Type>, typename my_tuple<N-1, Type>::type>::type;
};
template <class Type> struct my_tuple <1, Type>
{
using type = std::tuple<Type>;
};
Then my_typle<3, std::string>::type gives the type std::tuple<std::string, std::string, std::string>.
I'm writing a template that can convert a c style multidimensional array to a std array and declare it.
get c-style array matrix
template <typename, typename IS = std::index_sequence<>>
struct unpacked_array_type_impl
{
using type = IS;
};
template <typename T, std::size_t ... I>
struct unpacked_array_type_impl<T*, std::index_sequence<I...>>
: public unpacked_array_type_impl<T, std::index_sequence<0u, I...>>
{ };
template <typename T, std::size_t N, std::size_t ... I>
struct unpacked_array_type_impl<T[N], std::index_sequence<I...>>
: public unpacked_array_type_impl<T, std::index_sequence<I..., N>>
{ };
template<typename T>
struct unpacked_array_type
{
using type = typename unpacked_array_type_impl<T>::type;
};
std array declaration
template<typename T, size_t size, size_t... more>
struct myArray_impl
{
using type = std::array<typename myArray_impl<T, more...>::type, size>;
};
template<typename T, size_t size>
struct myArray_impl<T, size>
{
using type = std::array<T, size>;
};
template<typename T, size_t size, size_t... more>
using myArray = typename myArray_impl<T, size, more...>::type;
Below is the code I want to achieve.
using example_type = int[4][10];
using real_type = std::remove_all_extents<example_type>::type
myArray<real_type, unpacked_array_type<example_type>::type> this_would_be_std_arr;
But I got C2993 error.
The problem is that std::integer_sequence cannot expand to the variable argument of myArray template.
I would appreciate if you can help me.
First template return std::index_sequence, whereas the second take a list of number.
You might use std::index_sequence for both:
template<typename T, typename Seq>
struct myArray_impl;
template<typename T, size_t size, size_t... more>
struct myArray_impl<T, std::index_sequence<size, more...>>
{
using type = std::array<typename myArray_impl<T, std::index_sequence<more...>>::type, size>;
};
template<typename T, size_t size>
struct myArray_impl<T, std::index_sequence<size>>
{
using type = std::array<T, size>;
};
template<typename T, typename Seq>
using myArray = typename myArray_impl<T, Seq>::type;
Demo.
Instead of trying to build a stack of array extents with std::index_sequence and apply them to a template parameter pack, why not just define your specialization of myArray_impl to recursively deduce each extent from typename T?
template <typename T>
struct myArray_impl
{
using type = typename std::remove_cv<T>::type;
};
template <typename T, std::size_t N>
struct myArray_impl<T[N]>
{
using type = std::array<typename myArray_impl<T>::type, N>;
};
template <typename T>
using myArray = typename myArray_impl<T>::type;
Then you get the following example type aliases:
myArray<const int[4]> // std::array<int, 4>>
myArray<float[4][10]> // std::array<std::array<float, 10>, 4>>
I'm making a tensor class that can make tensors of any order. For example a third order 3 by 2 by 4 float tensor is made using Tensor<float, 3, 2, 4> a. The elements are recursively stored as arrays of lower order tensors, until the 1st order, at which point they're stored as an array of the given type.
template <typename T, int m, int ...n>
class Tensor {
public:
std::array<std::conditional<sizeof...(n) == 0, T, Tensor<T, n...>>, m> a;
};
I have a main loop to test it out.
#include <iostream>
#include <typeinfo>
int main() {
Tensor<float, 3, 2, 4> a;
// Tensor<float, 3> b;
std::cout << typeid(a.a[0]).name() << '\n';
return 0;
}
For some reason, constructing b fails but a succeeds. Also a.a[0] should simply be of type Tensor<float, 2, 4>, but instead it's some conditional type, at runtime. How is this even possible? Don't understand what's going on here, both why b fails with a succeeding (seems impossible) and why the types are conditionals at runtime.
Instantiating std::conditional<sizeof...(n) == 0, T, Tensor<T, n...>> instantiates Tensor<T, n...> even when the condition is true, meaning you give the template one argument instead of the 2+ it requires. Lazy arguments don't exist in C++ for general use (including standard library templates). You need to delay the instantiation of the problematic template so that you instantiate it only when applicable. I find the easiest way to do that is with if constexpr:
template<typename T>
static T type();
static auto determine_element_type() {
if constexpr (sizeof...(n) == 0) {
return type<T>();
} else {
return type<Tensor<T, n...>>();
}
}
std::array<decltype(determine_element_type()), m> a;
Your type of a was off because you used the std::conditional type itself instead of std::conditional_t or typename std::conditional<...>::type to get the resulting type.
std::conditional is a type trait. The way a type trait works, by convention, is that the result of the type-level computation is a member type alias of the std::conditional class, which otherwise is useless. I.e. std::conditional is basically defined like this:
template<bool, typename, typename>
struct conditional;
template<typename T, typename F>
struct conditional<true, T, F> { using type = T; };
template<typename T, typename F>
struct conditional<false, T, F> { using type = F; };
There are also template aliases to reduce typing:
template<bool B, typename T, typename F>
using conditional_t = typename std::conditional<B, T, F>::type;
And you're meant to use it like this
template <typename T, int m, int ...n>
struct Tensor {
std::array<std::conditional_t<sizeof...(n) == 0, T, Tensor<T, n...>>, m> a;
};
This doesn't actually work: if you try to instantiate Tensor<float, 3>, then that tries to instantiate Tensor<float>, and that fails, since Tensor must have at least one int. It doesn't matter that the use of Tensor<float> is under std::conditional_t. std::conditional_t is not like a ? : conditional. It is like a function
Type conditional_t(bool b, Type t, Type f) { return b ? t : f; }
The "arguments" are evaluated before the conditional switches between them. If one of the arguments is ill-formed, the conditional will not save you.
A better design would be specialization of the whole class.
template<typename T, int... ns>
struct tensor;
// a rank-0 tensor is a scalar
template<typename T>
struct tensor<T> {
T value;
};
// a rank-(n+1) tensor is some number of rank-n tensors
template<typename T, int n, int... ns>
struct tensor<T, n, ns...> {
std::array<tensor<T, ns...>, n> value;
};
Not sure what you meant by Tensor<float, 3, 2, 4> working in your version; didn't work for me!
Here's a neat, complete example
You can define the tensor as nested arrays like this:
#include <array>
#include <type_traits>
namespace detail {
template <class T, std::size_t n, std::size_t... ns>
auto helper() {
if constexpr (sizeof...(ns) == 0)
return std::array<T, n>();
else
return std::array<decltype(helper<T, ns...>()), n>();
}
}
template <class T, std::size_t n, std::size_t... ns>
using Tensor = decltype(detail::helper<T, n, ns...>());
int main()
{
Tensor<float, 2, 3, 4> b;
b[0][1][2] = 1;
}
I'm working on an Arduino project, which means the C++ dialect is currently the gnu++11 superset of C++11, and stdlib is not available (no tuples, no arrays, no nothing; namespace std is just empty!).
For optimization reasons (the CPU has 16K of FLASH, 2K of RAM and this particular low-voltage version runs at 8MHz) I want the compiler to pre-compute as much as possible to provide runtime code, especially the interrupt service routines, with "friendly" data.
Now what I would like to do is the following:
given a list of (unique) integers, I want to extract the values that match an arbitrary filter.
Then I want to build an index table that will allow to reach the filtered elements through their initial indices
For instance 2,10,4,7,9,3 with the filter value < 8 could yield the filtered list 2,4,7,3 and the index table 0,-1,1,2,-1,3.
The order of the elements in the filtered array does not matter as long as the index table remains consistent.
I insist on the fact that I want constant arrays. Producing these data dynamically would be trivial, but I want the compiler to do the job, without executing a single instruction at runtime.
The initial list would be given by a plain #define, and the results would be in constant arrays, e.g:
#define my_list 2,10,4,7,9,3
constexpr bool filter (int value) { return value < 8; }
const int filtered_list [] = filter_list <filter>(my_list);
const size_t filtered_index[] = filter_index<filter>(my_list);
The question is, how to implement these filter_list and filter_index templates with barebone C++11 and no stdlib, if at all feasible?
I'm not interested in error handling, the abnormal cases like empty lists or duplicate values are already taken care of. I'd rather like to see the simplest possible implementation, even if some assumptions are made about data validity.
The exact form of the templates, filter or the initial list do not matter either. All that matters is to get the arrays from an unique list definition.
For instance I would not mind a syntax where each element of the list is declared separately (though I can't really imagine how that could work).
I would prefer to have a self-contained C++ source. On the other hand, if what Python could achieve in a couple dozen lines requires pages of cryptic templates, including the rewriting of std::array and std::tuple, I'd rather write some external preprocessor.
Here is a small implementation of compile-time filtering, reproducing small parts of the standard library in a minimalist way. It includes an example of usage at the end. (It probably isn't possible to implement the filtering as a function, since C++ doesn't allow the result type of a function to depend on the values of the arguments. So, you would have to have the result type have enough storage for the case where the predicate always returns true which seems like it would be a show-stopper for your use case. That is why the approach here is to do the filtering using template metaprogramming first, and then convert the results to an array wrapper object.)
#include <sys/types.h>
template <typename T, size_t N>
struct array {
T elem[N];
constexpr size_t size() const { return N; }
constexpr T operator[](size_t i) const { return elem[i]; }
T* begin() { return elem; }
const T* begin() const { return elem; }
T* end() { return elem + N; }
const T* end() const { return elem; }
};
template <typename T>
struct array<T, 0> {
constexpr size_t size() const { return 0; }
T* begin() { return nullptr; }
const T* begin() const { return nullptr; }
T* end() { return nullptr; }
const T* end() const { return nullptr; }
};
template <typename T, T... x>
struct static_sequence { };
template <bool p, typename TrueT, typename FalseT>
struct conditional;
template <typename TrueT, typename FalseT>
struct conditional<true, TrueT, FalseT> {
using type = TrueT;
};
template <typename TrueT, typename FalseT>
struct conditional<false, TrueT, FalseT> {
using type = FalseT;
};
template <bool p, typename TrueT, typename FalseT>
using conditional_t = typename conditional<p, TrueT, FalseT>::type;
template <typename T, T x, typename S>
struct static_sequence_cons;
template <typename T, T x, T... Ss>
struct static_sequence_cons<T, x, static_sequence<T, Ss...>> {
using type = static_sequence<T, x, Ss...>;
};
template <typename T, T x, typename S>
using static_sequence_cons_t = typename static_sequence_cons<T, x, S>::type;
template <typename T, bool(*pred)(T), T... N>
struct filter;
template <typename T, bool(*pred)(T)>
struct filter<T, pred> {
using type = static_sequence<T>;
};
template <typename T, bool(*pred)(T), T hd, T... tl>
struct filter<T, pred, hd, tl...> {
private:
using filter_tl = typename filter<T, pred, tl...>::type;
public:
using type = conditional_t<pred(hd),
static_sequence_cons_t<T, hd, filter_tl>,
filter_tl>;
};
template <typename T, bool(*pred)(T), T... N>
using filter_t = typename filter<T, pred, N...>::type;
template <ssize_t curr_index, typename T, bool(*pred)(T), T... N>
struct filter_index;
template <ssize_t curr_index, typename T, bool(*pred)(T)>
struct filter_index<curr_index, T, pred> {
using type = static_sequence<ssize_t>;
};
template <ssize_t curr_index, typename T, bool(*pred)(T), T hd, T... tl>
struct filter_index<curr_index, T, pred, hd, tl...> {
using type = conditional_t<pred(hd),
static_sequence_cons_t<ssize_t, curr_index, typename filter_index<curr_index + 1, T, pred, tl...>::type>,
static_sequence_cons_t<ssize_t, -1, typename filter_index<curr_index, T, pred, tl...>::type>>;
};
template <typename T, bool(*pred)(T), T... N>
using filter_index_t = typename filter_index<0, T, pred, N...>::type;
template <typename T, T... x>
constexpr array<T, sizeof...(x)> static_sequence_to_array(
static_sequence<T, x...>) {
return array<T, sizeof...(x)> { x... };
}
//
// EXAMPLE USAGE
//
constexpr bool even(int n) {
return n % 2 == 0;
}
constexpr auto x = static_sequence_to_array(
filter_t<int, even, 0, 1, 2, 3, 4>{});
constexpr auto i = static_sequence_to_array(
filter_index_t<int, even, 0, 1, 2, 3, 4>{});
static_assert(x.size() == 3, "Bad filter");
static_assert(x[0] == 0, "Bad filter");
static_assert(x[1] == 2, "Bad filter");
static_assert(x[2] == 4, "Bad filter");
static_assert(i.size() == 5, "Bad filter_index");
static_assert(i[0] == 0, "Bad filter_index");
static_assert(i[1] == -1, "Bad filter_index");
static_assert(i[2] == 1, "Bad filter_index");
static_assert(i[3] == -1, "Bad filter_index");
static_assert(i[4] == 2, "Bad filter_index");
There is a way to avoid most of the boilerplate using function templates instead of full classes. The last class template is needed because there is no return type deduction for functions in c++11. int is used instead of typename T to skip unimportant template parameters. The code could be slimmed further when atmel updates their toolchain to gcc5 or newer with c++14 support.
#define LIST 2,10,4,7,9,3
constexpr bool less8(int v) { return v < 8; }
typedef bool(*predicate)(int);
template<int... values>
struct seq {};
template<int N>
struct array {
const int data[N];
};
template<int... values>
constexpr array<sizeof...(values)> to_array(seq<values...>) { return {{ values... }}; }
template<typename trueType, typename falseType>
constexpr falseType select(seq<0>, trueType, falseType) { return {}; }
template<typename trueType, typename falseType>
constexpr trueType select(seq<1>, trueType, falseType) { return {}; }
template<int... first, int... second>
constexpr seq<first..., second...> append(seq<first...>, seq<second...>) { return {}; }
template<predicate p, typename N, typename V>
struct filter_impl;
template<predicate p, int next>
struct filter_impl<p, seq<next>, seq<>> {
using type = seq<>;
};
template<predicate p, int next, int first, int... rest>
struct filter_impl<p, seq<next>, seq<first, rest...>> {
using type = decltype(
append(
select(seq<p(first)>{}, seq<next>{}, seq<-1>{}),
typename filter_impl<p, decltype(select(seq<p(first)>{}, seq<next+1>{}, seq<next>{})), seq<rest...>>::type{}
)
);
};
extern constexpr auto const_indices = to_array(filter_impl<less8, seq<0>, seq<LIST>>::type{});
My own answer just to put things together from a mundane point of view.
It is heavily based on Daniel Schepler's solution. I certainly would still be stuck on this problem without his help. Rewriting my own version was more like a learning exercise.
//////////////////////////////////////////////////////////////////////////////
// should allow to compile outside Arduino environment without std includes
typedef unsigned char uint8_t;
typedef unsigned size_t;
//////////////////////////////////////////////////////////////////////////////
// pseudo-stl
// barebone std::array
template <typename T, size_t N> struct array {
T elem[N];
constexpr size_t size() { return N; }
constexpr T operator[](size_t i) { return elem[i]; }
};
// barebone std::integer_sequence
template <typename T, T... Values> struct integer_sequence
{
typedef T value_type;
};
//////////////////////////////////////////////////////////////////////////////
// sequence filtering
// predicate functions prototype (means the sequence type must be convertible to int)
typedef bool(*predicate)(int);
// LISP-like 'if' (selects a parameter according to a boolean value)
template <bool Check, typename IfTrue, typename IfFalse> struct s_if;
template <typename IfTrue, typename IfFalse> struct s_if<true , IfTrue, IfFalse> {using type = IfTrue ;};
template <typename IfTrue, typename IfFalse> struct s_if<false, IfTrue, IfFalse> {using type = IfFalse;};
template <bool Check, typename IfTrue, typename IfFalse>
using f_if = typename s_if<Check, IfTrue, IfFalse>::type;
// LISP-like 'cons' for integer_sequence
template <typename T, T Car, typename Cdr> struct s_integer_sequence_cons;
template <typename T, T Car, T... Cdr> struct s_integer_sequence_cons<T, Car, integer_sequence<T, Cdr...>>
{ using type = integer_sequence<T, Car, Cdr...>; };
template <typename T, T Car, typename Cdr>
using f_cons = typename s_integer_sequence_cons<T, Car, Cdr>::type;
// LISP-like 'append' for integer_sequence
template <typename T, typename S1, typename S2> struct s_integer_sequence_append;
template <typename T, T... S1, T... S2> struct s_integer_sequence_append<T, integer_sequence<T, S1...>, integer_sequence<T, S2...>>
{ using type = integer_sequence<T, S1..., S2...>; };
template <typename S1, typename S2>
using f_append = typename s_integer_sequence_append<S1::value_type, S1, S2>::type;
// filter an integer_sequence according to a predicate
template <typename Sequence, predicate pred> struct s_filter;
template <typename T, predicate pred> struct s_filter<integer_sequence<T>, pred>
{
using type = integer_sequence<T>; // termination condition
};
template <typename T, T Car, T...Cdr, predicate pred> struct s_filter<integer_sequence<T, Car, Cdr...>, pred>
{
using tail = typename s_filter<integer_sequence<T, Cdr...>, pred>::type; // forward recursion on the sequence tail
using type = f_if<pred(Car), // if current element satisfies the predicate
f_cons<T, Car, tail>, // add it to the list
tail>; // else skip it
};
template <typename Sequence, predicate pred>
using f_filter = typename s_filter<Sequence, pred>::type;
//////////////////////////////////////////////////////////////////////////////
// now for the indexation...
// returns the index of a value in a list of values, or -1 if not found
template <int I , typename T> constexpr T find_index (T elem) { return -1; }
template <int I=0, typename T, typename... List> constexpr T find_index (T elem, T val, List... rest)
{ return elem == val ? I : find_index<I+1>(elem, rest...); }
// builds an index list allowing to reach each value final position from their initial position
template <typename Target, typename Origin> struct s_index_list;
template <typename T, typename Origin> struct s_index_list<integer_sequence<T>, Origin>
{
using type = integer_sequence<T>; // termination of the Target list
};
template <typename T, T Car, T... Cdr, T...Origin> struct s_index_list<integer_sequence<T, Car, Cdr...>, integer_sequence<T, Origin...>>
{
// as usual, the only way to loop is to recurse...
using tail = typename s_index_list<integer_sequence<T, Cdr...>, integer_sequence<T, Origin...>>::type;
using type = f_cons<T, find_index(Car, Origin...), tail>;
};
template <typename Target, typename Origin>
using f_index = typename s_index_list<Target, Origin>::type;
//////////////////////////////////////////////////////////////////////////////
// implementing sequences as arrays
// turn an integer_sequence into a (constant) array
template <typename T, T... x> constexpr array<T, sizeof...(x)> integer_sequence_to_array(integer_sequence<T, x...>)
{ return array<T, sizeof...(x)> { x... }; }
//////////////////////////////////////////////////////////////////////////////
// Putting all this marvelous piece of engineering to use
//
// our initial list
#define input_list 2,10,4,7,9,3
// convert the list into a sequence
typedef integer_sequence<uint8_t, input_list> input_sequence;
// define filtering predicates
constexpr bool test_group_1(int n) { return (n >> 3) == 0; } // values from 0 to 7
constexpr bool test_group_2(int n) { return (n >> 3) == 1; } // values from 8 to 15
// compute the two split sequences
typedef f_filter<input_sequence, test_group_1> sequence_1; // <unsigned char, 2u, 4u, 7u, 3u>
typedef f_filter<input_sequence, test_group_2> sequence_2; // <unsigned char, 10u, 9u>
// append them
typedef f_append<sequence_1, sequence_2> output_sequence; // <unsigned char, 2u, 4u, 7u, 3u, 10u, 9u>
// compute indexes
typedef f_index<output_sequence, input_sequence> output_indexes; // <unsigned char, 0u, 2u, 3u, 5u, 1u, 4u>
// turn the results into arrays
constexpr auto const_values = integer_sequence_to_array(output_sequence{}); // [2, 4, 7, 3,10, 9]
constexpr auto const_indexes = integer_sequence_to_array(output_indexes {}); // [0, 2, 3, 5, 1, 4]
A few afterthoughts
The purpose of this code is to generate a couple of integer arrays at compile time. This could easily be done with 20 lines of any language able to handle text files (Python, PHP, Perl, awk, you name it...), doing the trivial filtering and indexing in a couple of loops and replacing the line
#define my_list 2,10,4,7,9,3
with
const int filtered_list [] = {2,4,7,3};
const size_t filtered_index[] = {0,-1,1,2,-1,3};
before passing the modified source to the actual C++ compiler.
The only reason I wanted a self-contained C++ program is the Arduino environment. It is meant to be very simple, and as such is considerably restrictive. You can't really ask a casual Arduino programmer to tweak makefiles or use external code generation tools, so if you want to provide an "Arduino-friendly" module you're basically stuck with this stdlib-less gnu++11 compiler.
Now since the C++ preprocessor is clearly not up to that kind of job, the only choice left is template metaprogramming.
I can't say I really understand how it works, I just used it as a last resort. I'm an embedded software programmer, and my functional programming skills are certainly nothing to write home about.
Still I tried to put my rusty LISP notions to good use, but basic tools like cons, append or if were apparently nowhere to be found, and re-writing them from scratch felt largely like trying to reinvent the wheel. Having to coax the pattern-matching engine into recursion to implement simple loops was also rather painful and made for pretty eye-watering code. Hopefully there's a few useful tricks I missed there.
All this being said, and to my surprise, the lack of stdlib was not really a problem. You can whip up the bare minimum in a few lines of code, provided you throw caution to the wind and kiss the syntactic sugar goodbye.
I finally came up with something that actually does the job in a bit less than 100 lines of very obfuscated code.
I'm not looking for efficiency here, my lists will hardly hold more than a dozen values, but I sure would be happy to watch and learn a way to achieve the same result with less source code. Any takers?
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
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
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]