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());
};
Related
My goal is to be able to compare templates i.e. write something like this
template<typename T>
struct template_type;
template<template <typename...> typename TTmpl, typename ...Ts>
struct template_type<TTmpl<Ts...>> { using type = TTmpl; }; // [1] this isn't valid C++
template<typename TRng, typename T>
auto find(TRng&& rng, const T& val)
{
using TTmpl = typename mcu::template_type<std::remove_const_t<std::remove_reference_t<TRng>>>::type;
if constexpr (std::is_same_v<TTmpl, std::set>) // [2] this isn't valid C++
{
return rng.find(val);
}
else
{
using namespace std;
return std::find(begin(rng), end(rng), val);
}
}
My second attemp was to use alias template and custom same function. Something like:
template<typename T>
struct template_type;
template<template <typename...> typename TTmpl, typename ...Ts>
struct template_type<TTmpl<Ts...>> { template<typename ...Us> using type = TTmpl<Us...>; };
template<template<typename...>typename T1, template<typename...> typename T2>
struct same : std::false_type {};
template<template<typename...>typename T>
struct same<T, T> : std::true_type {};
But this doesn't work either. The problem is that same<std::set, template_type<std::set<int>>::type>::value returns false, i.e. template class and it's template alias are different for some reason.
My third attemp was to write something like template_type_identity but I cannot figure out what should I use as identity:
template<template<typename...> typename T>
struct template_type_identity { using type = ???; }; // I could hardcode some constexpr ids for each template but I wanna generic solution
I am not sure if I understand what you want, also because I would use a different solution for your motivating case (sfinae on the presence of member find). Anyhow, this is how you can check if a given type is an instantiation of a template, provided the template has only type parameters:
#include <iostream>
#include <type_traits>
#include <set>
#include <vector>
template <template<typename...> typename T, typename C>
struct is_instantiation_of : std::false_type {};
template <template<typename...> typename T,typename ...P>
struct is_instantiation_of< T,T<P...>> : std::true_type {};
int main(){
std::cout << is_instantiation_of<std::set,std::set<int>>::value;
std::cout << is_instantiation_of<std::set,std::vector<int>>::value;
}
Output:
10
template<int Degree> using curve = // some definition
template<int MaxDegree> using curve_variant = std::variant<curve<1>, curve<2>, .. curve<MaxDegree>>;
In the above example, the number of parameters passed to std::variant's template would change depending on curve_variant's parameters:
For example, curve_variant<4> would resolve to std::variant<curve<1>, curve<2>, curve<3>, curve<4>>.
Since MaxDegree is known at compile time, this feels possible. But I also don't have a clue how to begin implementing this.
With std::integer_sequence helper, you might do:
template <typename Seq> struct curve_variant_impl;
template <int ... Is>
struct curve_variant_impl<std::integer_sequence<int, Is...>>
{
using type = std::variant<curve<1 + Is>...>;
};
template <int MaxDegree>
using curve_variant = typename curve_variant_impl<std::make_integer_sequence<int, MaxDegree>>::type;
As the other answers show std::integer_sequence is a nice tool. Suppose we didn't have it.
The following is only to illustrate what code we would have to write if we didn't have std::integer_sequence. As a matter of fact, there is no reason to write it this way, if you do not have C++14, you can reimplement is easily.
#include <variant>
#include <type_traits>
template<int Degree> struct curve{};
// helper to add a type to a variant
template <typename A,typename... others>
struct merge_variants {
using type = std::variant<others...,A>;
};
template <typename A,typename... others>
struct merge_variants<A,std::variant<others...>> : merge_variants<A,others...> {};
// the recursion:
template <int MaxDegree>
struct Foo {
using type = typename merge_variants< curve<MaxDegree>,typename Foo<MaxDegree-1>::type >::type;
};
// the base case:
template <>
struct Foo<1> {
using type = std::variant< curve<1> >;
};
int main() {
static_assert(std::is_same<std::variant<curve<1>,curve<2>,curve<3>> , Foo<3>::type >::value);
}
Recursion is rather expensive, to instantiate Foo<N> (sorry for the name) N other types have to be instantiated, even though we never asked for them. std::integer_sequence can avoid the recursion completely.
#include <utility>
#include <variant>
template<int Degree>
struct curve{};
template<typename index_seq>
struct curve_variant_impl;
template<int...indices>
// Start binding indices from 1, not zero
struct curve_variant_impl<std::integer_sequence<int,0,indices...>>{
using type = std::variant<curve<indices>...>;
};
template<int MaxDegree>
//make_integer_sequence makes [0,MaxDegree), we want [1,MaxDegree]
using curve_variant = typename curve_variant_impl<std::make_integer_sequence<int,MaxDegree+1>>::type;
int main() {
static_assert(std::is_same_v<curve_variant<4>,std::variant<curve<1>, curve<2>, curve<3>, curve<4>>>);
}
The above works only with non-negative values, so you might as well use std::size_t which is natural type for indices.
Just to go with the other answers, if you have C++11 but not C++14, you can emulate std::integer_sequence with a clever trick:
template <int...>
struct seq { };
template <int N, int... S>
struct gens : gens<N - 1, N - 1, S...> { };
template <int... S>
struct gens<0, S...> {
typedef seq<S...> type;
};
With this pattern, gens operates like integer_sequence does. gens<N>::type is seq<1, 2, 3, ..., N>. Thus, you use it just the same
template <typename T>
struct make_curve_variant_impl
template <int... N>
struct make_curve_variant_impl<seq<N...>>
{
typef std::variant<curve<N+1>...> type;
};
template <typename N>
struct make_curve_variant
{
typedef make_curve_variant_impl<typename gens<N>::type>:: type;
};
I fully expect this to not be a feature, but figured I may as well ask; is it possible to expand code at compile time using template parameters?
For example:
template <size I>
void foo()
{
...double... vec;
}
Where the ... Is replaced by std::vector< >, I times.
So foo<2>() would compile to:
void foo()
{
std::vector<std::vector<double>> vec;
}
I can't even imagine what the syntax for this would be, so I'm not hopeful.
It would be useful for something like an N dimensional binning class, which could also be implemented through recursion, but not so neatly imo.
Yes, you can. You can do it with class templates and specializations, like this:
template<std::size_t N>
struct MultiDim {
using underlying = typename MultiDim<N-1>::type;
using type = std::vector<underlying>;
};
template<>
struct MultiDim<1> {
using type = std::vector<double>;
};
template<std::size_t N>
using multi_dimensional = typename MultiDim<N>::type;
Hence, multi_dimensional<1> is vector<double>, and multi_dimensional<N> where N>1 is vector<multi_dimensional<N-1>>.
is it possible to expand code at compile time using template parameters?
Directly... I don't think so.
But I suppose you can implement a specific type traits as follows
template <typename T, std::size_t I>
struct bar
{ using type = std::vector<typename bar<T, I-1U>::type>; };
template <typename T>
struct bar<T, 0U>
{ using type = T; };
and use in foo() in this way
template <std::size_t I>
void foo ()
{
typename bar<double, I>::type vec;
}
If you want to be a little more generic, you can also pass std::vector as a template-template parameter; if you define bar as follows
template <template <typename...> class C, typename T, std::size_t I>
struct bar
{ using type = C<typename bar<C, T, I-1U>::type>; };
template <template <typename ...> class C, typename T>
struct bar<C, T, 0U>
{ using type = T; };
foo() become
template <std::size_t I>
void foo ()
{
typename bar<std::vector, double, I>::type vec;
}
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]
I have a question about templates and it is in the code:
template<typename T>
struct foo {
T t;
};
template<typename FooType>
struct bar {
T t; //<- how to get T here (preferably without using typedef in foo)
};
Here's a generic template argument type extractor:
#include <tuple>
template <typename> struct tuplify;
template <template <typename...> class Tpl, typename ...Args>
struct tuplify<Tpl<Args...>>
{
using type = std::tuple<Args...>;
};
template <typename T, unsigned int N>
using get_template_argument
= typename std::tuple_element<N, typename tuplify<T>::type>::type;
Usage:
get_template_argument<std::vector<int>, 1> a; // is a std::allocator<int>
Or in your case:
get_template_argument<FooType, 0> t;
If I understood your question correctly, you could use template specialization as follows. Given your foo<> class template:
template<typename T>
struct foo {
T t;
};
Define a bar<> primary template and a corresponding specialization this way:
template<typename FooType>
struct bar;
template<typename T>
struct bar<foo<T>> {
T t; // T will be int if the template argument is foo<int>
};
Under the assumption that you are always supposed to instantiate bar by providing an instance of foo<> as the type argument, you can leave the primary template undefined.
The specialization will match the foo<T> pattern, thus giving you the type with which foo<> is instantiated in T.
Here is how you could test the validity of this approach with a simple program:
#include <type_traits>
int main()
{
bar<foo<int>> b;
// This will not fire, proving T was correctly deduced to be int
static_assert(std::is_same<decltype(b.t), int>::value, "!");
}
Here is the corresponding live example.
If you don't want or can't add a typedef to foo, you can additionally write an independent "extractor" template
template <typename T> struct ExtractT;
template <typename T> struct ExtractT<foo<T> > {
typedef T type;
};
and use it as
template<typename FooType>
struct bar {
typename ExtractT<FooType>::type t;
};
You can take that ExtractT one step further and decouple it from foo
template <typename T> struct ExtractT;
template <template <typename> class C, typename T> struct ExtractT<C<T> > {
typedef T type;
};
and so on until you reinvent something from Boost or C++11 standard library :) BTW, this feels like something that should already be available in form of a more generic solution....