Template type defined array initialisation - c++

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]

Related

Template data member

I have this class
class Store
{
template <int N> using map = std::unordered_map<typename Topo<N>::type*, std::unique_ptr<Node<N>>>;
template <int N> map<N>& get();
map<0> m_0;
template <> map<0>& get<0>() { return m_0; }
map<1> m_1;
template <> map<1>& get<1>() { return m_1; }
// ...
map<7> m_7;
template <> map<7>& get<7>() { return m_7; }
};
I am looking for a way to avoid to repeat the declaration of the variables. Probably it is possible to do something with integer sequences, but I cannot figure out the syntax.
How about putting them in a heterogeneous array (i.e. tuple)?
class Store
{
public:
template <int N> using map = std::unordered_map<typename Topo<N>::type*, std::unique_ptr<Node<N>>>;
template <int N> map<N>& get();
std::tuple<map<0>, map<1>, ..., map<7>> maps;
};
Store store;
auto& m_1 = std::get<1>(store.maps);
...
You can have a getter if you want:
template<int N>
const auto& get_map() const {
return std::get<N>(maps);
}
As suggested by HolyBlackCat, you can use std::make_index_sequence to declare a std::tuple< map<0> , map<1> ,... map<N>>. You do not need getters. When they return non-const references you can make the members public.
#include <memory>
#include <tuple>
#include <utility>
template <int N> struct Foo {};
template <typename X> struct foo_tuple_impl;
template <int...I> struct foo_tuple_impl< std::integer_sequence<int,I...> > {
using type = std::tuple< Foo<I> ...>;
};
template <int N> struct foo_tuple {
using type = typename foo_tuple_impl< std::make_integer_sequence<int,N> >::type;
};
template <int N> using foo_tuple_t = typename foo_tuple<N>::type;
struct Storage {
foo_tuple_t<8> map;
};

std::is_same_v with unspecialized template

I have the following code:
#include <iostream>
#include <type_traits>
using namespace std;
template<typename T, int N>
class A {
public:
static constexpr int n = N;
};
template<typename T, typename X, int N>
class B {
public:
static constexpr int x = N;
};
template<typename T>
void test(T) {
if constexpr (std::is_same_v<T, A>) {
int a = T::n;
} else if constexpr (std::is_same_v<T, B>) {
int a = T::x;
}
cout << "a";
};
int main()
{
A<int, 2> a;
test(a);
return 0;
}
Compiling it produces the following error:
error: type/value mismatch at argument 2 in template parameter list for ‘template<class _Tp, class _Up> constexpr const bool std::is_same_v<_Tp, _Up>’
20 | if constexpr (std::is_same_v<T, A>) {
| ~~~~~^~~~~~~~~~~~~~~
note: expected a type, got ‘A’
The Problem is that I cannot use a correct type here (like A), since I don't know what the template parameters are or how many there are. Basically I want to match any type of class A with any template arguments.
You need to write your own trait for that:
template<typename>
struct is_specialization_of_A : std::false_type {};
template<typename T, int N>
struct is_specialization_of_A<A<T,N>> : std::true_type {};
template<typename T>
inline constexpr auto is_specialization_of_A_v = is_specialization_of_A<T>::value;
and analogously for B.
You could take the template as template template parameter of the trait as well, so that you only need one trait definition, but that only works as long as the template parameter categories of the templates match. This is not the case here (A has <type, non-type>, while B has <type, type, non-type>).
template<typename, template<typename, auto> class>
struct is_specialization_of : std::false_type {};
template<template<typename, auto> class Tmpl, typename T, auto N>
struct is_specialization_of<Tmpl<T, N>, Tmpl> : std::true_type {};
template<typename T, template<typename, auto> class Tmpl>
inline constexpr auto is_specialization_of_v = is_specialization_of<Tmpl, T>::value;
This can be used as is_specialization_of_v<A, T>, but not is_specialization_of_v<B, T>.
If you have many class templates like A and B, you can define a single type trait that would return a "tag":
struct tag_A {};
struct tag_B {};
template<class>
struct get_tag {
using type = void;
};
template<typename T, int N>
struct get_tag<A<T, N>> {
using type = tag_A;
};
template<typename T, typename X, int N>
struct get_tag<B<T, X, N>> {
using type = tag_B;
};
template<typename T>
void test(T) {
using tag = typename get_tag<T>::type;
if constexpr (std::is_same_v<tag, tag_A>) {
// ...
} else if constexpr (std::is_same_v<tag, tag_B>) {
// ...
}
}

Getting the number of dimensions of a std::vector/std::array

Let's say I want a class/struct type, inheriting from integral_constant<size_t, N> where N is the dimension and the dimension is achieved as follows:
template<class T>
struct dimension;
template<class T>
struct dimension<vector<T>> : integral_constant<size_t, 1> {};
template<class T>
struct dimension<vector<vector<T>>> : integral_constant<size_t, 2> {};
And then
cout << dimension<vector<int>>::value; // 1
cout << dimension<vector<vector<int>>>::value; // 2
But obviously this is not perfect, as the number of dimensions can be a infinite (in theory). Is there a way to achieve a generic solution to this?
Suggestion: I went in this direction, but no further:
template<class T, class... Tn>
struct dimension<vector<Tn...>> : integral_constant<size_t, sizeof...(Tn)> {};
Since std::vector has other template parameters this wouldn't work.
A bit hard to define "what's a container". The below checks for value_type, iterator, and const_iterator nested typedefs. Tweak the void_t check as you want. (For instance, if you want only things that can be subscripted to be recognized as containers, then add decltype(std::declval<T&>()[0]) to the list.)
Note that dimension_impl's specialization calls dimension. this allows you to specialize dimension for container-like things you don't want to be treated as a container (std::string comes to mind).
template<class T> struct dimension;
namespace details {
template<class T, class = void>
struct dimension_impl {
static constexpr std::size_t value = 0;
};
template<class T>
struct dimension_impl<T, std::void_t<typename T::value_type,
typename T::iterator,
typename T::const_iterator>> {
static constexpr std::size_t value = 1 + dimension<typename T::value_type>::value;
};
}
template<class T>
struct dimension : std::integral_constant<std::size_t,
details::dimension_impl<T>::value> {};
You can do this for a std::vector (note that the template parameter list of a std::vector is longer than 1):
template<typename T>
struct dimension { static constexpr std::size_t value = 0; };
template<typename T, typename... V>
struct dimension<std::vector<T, V...>>{
static constexpr std::size_t value = 1 + dimension<T>::value;
};
This works instead for a std::array:
template<typename>
struct dimension { static constexpr std::size_t value = 0; };
template<typename T, std::size_t N>
struct dimension<std::array<T, N>>{
static constexpr std::size_t value = 1 + dimension<T>::value;
};
It follows a minimal, working example:
#include<vector>
#include<iostream>
template<typename T>
struct dimension { static constexpr std::size_t value = 0; };
template<typename T, typename... V>
struct dimension<std::vector<T, V...>>{
static constexpr std::size_t value = 1 + dimension<T>::value;
};
int main() {
std::cout << dimension<std::vector<std::vector<int>>>::value << std::endl;
}

Tuple of sequence

I would like to store a sequence of classes into a tuple and declare this sequence as a member of another class:
template<size_t id> class Foo {};
template<size_t N>
class FooContainer
{
std::tuple<Foo<0>, Foo<1>, ..., Foo<N>> tup; // build this type at compile time ??
};
I tried this:
template<size_t N>
class FooContainer
{
template<size_t... id>
struct FoosImpl {
constexpr FoosImpl(std::index_sequence<id...>) {};
using type = std::tuple<Foo<id>...>;
};
template<size_t N, typename Indices = std::make_index_sequence<N>>
using Foos = decltype(FoosImpl(Indices())::type);
Foos<N> tup;
};
But this doesn't compiles. GCC complains:
error: missing template arguments before ‘(’ token using Foos = decltype(FoosImpl(Indices())::type);
I thought the compiler wouldn't need the template to be specified and that it would deduce the integer sequence from Indices(). But this doesn't seem to be the case.
Here is a possible way of doing what you want:
#include <tuple>
template<size_t id> class Foo {};
template <size_t... Idx>
std::tuple<Foo<Idx>...> get_foos(std::index_sequence<Idx...>);
template <size_t N>
using foo_tuple = decltype(get_foos(std::make_index_sequence<N>{}));
template<size_t N>
class FooContainer {
foo_tuple<N> tup;
};
You cannot (currently) let the compiler deduce the class template parameters (as you would need with FoosImpl), but you can let it deduce the template parameters of a functions and use the return type.
Starting from your example, you can do this:
#include<tuple>
#include<utility>
#include<type_traits>
template<size_t id> class Foo {};
template<size_t N>
struct FooContainer
{
template<size_t... id>
static constexpr std::tuple<Foo<id>...> func(std::index_sequence<id...>) {}
using Foos = decltype(func(std::make_index_sequence<N>()));
Foos foos;
};
int main() {
static_assert(std::is_same<FooContainer<3>::Foos, std::tuple<Foo<0>, Foo<1>, Foo<2>>>::value, "!");
}
Or you could simply use additional parameter to your FooContainer with default value of std::index_sequence<0,...,N> like:
#include <utility>
#include <tuple>
#include <iostream>
#include <typeinfo>
template<size_t I> class Foo {};
template <size_t N, class = std::make_index_sequence<N>>
struct FooContainer;
template <size_t N, size_t... Is>
struct FooContainer<N, std::index_sequence<Is...>> {
using Foos = std::tuple<Foo<Is>...>;
Foos foos;
};
int main() {
std::cout << typeid(FooContainer<3>{}.foos).name() << std::endl;
}
Output:
$ g++ -std=c++14 example.cc
$ ./a.out
St5tupleII3FooILm0EES0_ILm1EES0_ILm2EEEE
$ c++filt -t St5tupleII3FooILm0EES0_ILm1EES0_ILm2EEEE
std::tuple<Foo<0ul>, Foo<1ul>, Foo<2ul> >
Edit:
As skypjack mentioned one can now use our FooContainer class in an unexpected way by passing the second parameter explicitly... If passing arbitrary sequences in the second parameter of FooContainer is undesired one can guard the code by adding static_assert as follows:
#include <utility>
#include <tuple>
#include <iostream>
#include <typeinfo>
#include <type_traits>
template<size_t I> class Foo {};
template <size_t N, class = std::make_index_sequence<N>>
struct FooContainer;
template <size_t N, size_t... Is>
struct FooContainer<N, std::index_sequence<Is...>> {
static_assert(std::is_same<std::make_index_sequence<N>, std::index_sequence<Is...>>::value, "passed index_sequence was not generated using std::make_index_sequence<N>");
using Foos = std::tuple<Foo<Is>...>;
Foos foos;
};
int main() {
std::cout << typeid(FooContainer<3>{}.foos).name() << std::endl;
}

Why doesn't this recursive template work?

So I've tried to create what I thought would be a simple recursive template to generate a large but trivial nested type:
#include <type_traits>
#include <typeinfo>
#include <boost/core/demangle.hpp>
template <typename T>
struct A {};
template <typename T, int N>
struct Nest
{
using type = Nest<A<T>, N-1>
};
template <typename T>
struct Nest<T, 0>
{
using type = T;
};
int main()
{
typename Nest<A<int>, 20>::type x;
std::cout << boost::core::demangle(typeid(x).name());
};
The output of this program is
Nest<A<A<int> >, 19>
I was expecting to see a list of A<int> nested within 19 layers of A<>. What is happening here, and what do I have to do to get the effect I am looking for?
Forgot the recursion step:
template <typename T, int N>
struct Nest
{
using type = Nest<A<T>, N-1>::type
};
May need a typename -- I don't have access to a compiler right now.
This is the cleanest way to do recursion like this:
template <typename T>
struct A {};
template <typename T, unsigned N>
struct Nest:Nest<A<T>,N-1> {};
You can read the above as "A Nest<T,N> is a Nest<A<T>,N-1>" if you speak fluent C++.
We then add the exception:
template <typename T>
struct Nest<T, 0> {
using type = T;
};
Next, us a using alias to get rid of typename spam elsewhere:
template <typename T, unsigned N>
using Nest_t = typename Nest<T,N>::type;
int main() {
Nest_t<int, 20> x;
std::cout << boost::core::demangle(typeid(x).name());
};