Template-defined number of template parameters (very meta) - c++

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;
};

Related

How can a recover the non-typename template argument from each type in a variadic pack of template classes?

I am trying to recover the non-typename template argument from a variadic pack of template classes (where each class has one non-type argument) so that I can use them as an integer sequence in another type.
The code below shows what I have. The integer sequence / member sequence is cribbed from tuple.
template<std::size_t... Integers>
struct size_t_sequence {
using type = size_t_sequence<Integers...>;
};
template <std::size_t, typename>
struct push_size_t_sequence;
template <std::size_t I, std::size_t... Integers>
struct push_size_t_sequence<I, size_t_sequence<Integers...>>
: size_t_sequence<I, Integers...> {};
template <std::size_t N, std::size_t Head, std::size_t... Tail>
struct make_size_t_sequence
: push_size_t_sequence<Head, typename make_size_t_sequence<N - 1, Tail...>::type>::type {};
template<std::size_t I, std::size_t... OneLeft>
struct make_size_t_sequence <2, I, OneLeft...> :
push_size_t_sequence<I, size_t_sequence<OneLeft...>>::type {};
template<typename... Members>
struct member_sequence {
using type = member_sequence<Members...>;
};
template <typename, typename>
struct push_member_sequence;
template <typename M, typename... Members>
struct push_member_sequence<M, member_sequence<Members...>>
: member_sequence<M, Members...> {};
template <std::size_t N, typename Head, typename... Tail>
struct make_member_sequence
: push_member_sequence<Head, typename make_member_sequence<N - 1, Tail...>::type>::type {};
template<typename M, typename... OneLeft>
struct make_member_sequence <2, M, OneLeft...> :
push_member_sequence<M, member_sequence<OneLeft...>>::type {};
template<typename>
struct unpack_sequence_impl;
template<template<std::size_t> class... T, std::size_t... DimensionSizes>
struct unpack_sequence_impl<member_sequence<T<DimensionSizes>...>> {
using member_types = member_sequence<T<DimensionSizes>...>;
using size_types = size_t_sequence<DimensionSizes...>;
};
template<typename... Members>
struct unpack_sequence :
unpack_sequence_impl<make_member_sequence<sizeof...(Members), Members...>> {
using base_t = unpack_sequence_impl<make_member_sequence<sizeof...(Members), Members...>>;
using member_types = typename base_t::member_types;
using size_types = typename base_t::size_types;
};
template<std::size_t N>
class example {
int s = N;
};
int main()
{
auto mem_sequence = make_member_sequence<3, example<3>, example<2>, example<1>>::type();
auto integer_sequence = make_size_t_sequence<3, 3,2,1>::type();
auto un_mem_sequence = unpack_sequence<example<3>, example<2>, example<1>>::member_types();
auto un_size_sequence = unpack_sequence<example<3>, example<2>, example<1>>::size_types();
}
The types of mem_sequence and integer_sequence are member_sequence<example<3>,example<2>,example<1>> and size_t_sequence<3,2,1>. The types of un_mem_sequence and un_size_sequence should be the same.
How can I accomplish this?
Thank you for your help!
Tim
Edit:
For clarification what I am trying to accomplish is recovering the template arguments from one template class to use them in another. Below are three template classes: MyObject, MyTuple, MyPack. MyTuple takes MyObject objects as its template parameters. I want to recover the MyObject template parameters to use as the template argument for a MyPack object.
template<int N>
MyObject;
template<int... Ns>
MyPack;
template<typename... MyObjects>
MyTuple {};
int main() {
MyTuple<MyObject<1>,MyObject<2>,MyObject<3>> a;
MyPack<1,2,3> b;
}
So I want to pull the arguments from the MyObject's in MyTuple to be used to create a MyPack.
Edit 2:
Second clarification: MyTuple does not just take MyObject types but any type that has one template parameter of int.
template<int N>
MyObject;
template<int N>
MyObject2;
template<int... Ns>
MyPack;
template<typename... MyObjects>
MyTuple {};
int main() {
MyTuple<MyObject<1>,MyObject<2>,MyObject2<1>,MyObject2<2>> a;
MyPack<1,2,1,2> b;
}
template <typename>
struct MakeMyPack;
template <int... Ns>
struct MakeMyPack<MyTuple<MyObject<Ns>...>>
{
using type = MyPack<Ns...>;
};
DEMO
For clarification what I am trying to accomplish is recovering the template arguments from one template class to use them in another. Below are three template classes: MyObject, MyTuple, MyPack. MyTuple takes MyObject objects as its template parameters. I want to recover the MyObject template parameters to use as the template argument for a MyPack object.
Just for fun, I propose a solution more generic, with a double variadic template (template-template and value template), not bounded to MyTuple and MyObject
template <typename>
struct getMyPack;
template <template <typename...> class C,
template <int> class ... Cs, int ... Is>
struct getMyPack<C<Cs<Is>...>>
{ using type = MyPack<Is...>; };
The following is a full compiling example
#include <type_traits>
template <int>
struct MyObject1
{ };
template <int>
struct MyObject2
{ };
template <int...>
struct MyPack
{ };
template <typename...>
struct MyTuple
{ };
template <typename>
struct getMyPack;
template <template <typename...> class C,
template <int> class ... Cs, int ... Is>
struct getMyPack<C<Cs<Is>...>>
{ using type = MyPack<Is...>; };
int main ()
{
using T0 = MyTuple<MyObject1<1>, MyObject2<2>, MyObject1<3>>;
using T1 = MyPack<1, 2, 3>;
using T2 = typename getMyPack<T0>::type;
static_assert( std::is_same<T1, T2>::value, "!" );
}
-- EDIT --
Second clarification: MyTuple does not just take MyObject types but any type that has one template parameter of int.
As I was suspecting.
My solution is unboud to MyObject so should works.
Preceding example modified to show it.

Compile time code expansion using template parameters

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;
}

C++11 Variadic Templates: 3..n int's separated into first, middle, last

As the title says I've got a Variadic Template that accepts at least 3 parameters (int's)
template<int p_first, int p_second, int p_third, int... p_rest>
and i need to split these into first, middle and last
class MyClass {
OtherClass<p_first> first;
// Works obviously
// std::vector<OtherClass> middle... Doesn't work
OtherClass<p_last> last;
// No idea how to do this
}
Visual Studio 2015 C++ features are available
Edit: Sorry I forgot to mention key aspects.
OtherClass implementation:
template <int v>
class OtherClass { ... };
Yes, I want all the values in the middle (between first and last)
"the vector can't hold different types of OtherClass" Thank you for reminding me of that. This might be the reason, that this isn't possible.
"Are you reinventing tuples?" Maybe using tuples is really a better solution. Looking into it.
Thank you for the posts. I'll understand, test the code and comment soon!
First, boilerplate. I'm working in types, not constants, because metaprogramming with types is far easier.
template<class T>struct tag{using type=T;};
template<class Tag>using type_t=typename Tag::type;
template<int I>using int_k=std::integral_constant<int, I>;
template<class...>struct types{using type=types;};
template<template<class...>class Z, class pack>
struct apply;
template<template<class...>class Z, class pack>
using apply_t=type_t<apply<Z,pack>>;
template<template<class...>class Z, class...Ts>
struct apply<Z, types<Ts...>>:tag<Z<Ts...>>{};
Now once we have a pack of middle elements, we can apply them.
template <std::size_t N, class... Ts>
using get_t = type_t< std::tuple_element< N, std::tuple<Ts...> > >;
gets the nth type from a list of types.
template <class Is, class pack>
struct get_slice;
template <class Is, class pack>
using get_slice_t=type_t<get_slice<Is,pack>>;
template<std::size_t...Is, class...Ts>
struct get_slice<std::index_sequence<Is...>,types<Ts...>>:
types< get_t<Is, Ts...>... >{};
This lets us take a pack, and get a slice from it.
Offset an index sequence:
template<class Is, std::size_t I>
struct offset;
template<class Is, std::size_t I>
using offset_t=type_t<offset<Is,I>>;
template<std::size_t...Is, size_t I>
struct offset<std::index_sequence<Is...>, I>:
tag<std::index_sequence<(I+Is)...>>
{};
Extract the middle elements starting at start of length len:
template<std::size_t start, std::size_t len, class pack>
struct get_mid:
get_slice< offset_t< std::make_index_sequence<len>, start >, pack >
{};
template<std::size_t start, std::size_t len, class pack>
using get_mid_t=type_t<get_mid<start,len,pack>>;
and now we can split your elements into first, last and stuff the rest in a tuple:
template<int p_first, int p_second, int p_third, int...is>
class MyClass {
using pack = types< int_k<p_first>, int_k<p_second>, int_k<p_third>, int_k<is>... >;
OtherClass<p_first> first;
using mid = get_mid_t<1, sizeof...(is)+1, pack >;
template<class...Ts>
using OtherClass_tuple = std::tuple<OtherClass<Ts::value>...>;
apply_t< OtherClass_tuple, mid > middle;
OtherClass<get_t<sizeof...(is)+2, pack>::value> last;
};
You could write a helper struct to get the Nth int from an int pack:
template <std::size_t N, int... I>
struct get_n :
std::integral_constant<int,
std::get<N>(std::array<int,sizeof...(I)> { I... })
>
{};
Then you could write metafunctions to get the middle and end:
template <int... I>
using get_middle = get_n<sizeof...(I)/2 - 1, I...>;
template <int... I>
using get_end = get_n<sizeof...(I) - 1, I...>;
You can use this like so:
using p_last = get_end<p_third, p_rest...>;
OtherClass<p_last> last;
If you want a tuple of OtherClass<N> for all the middle elements, here's a fairly simple solution. See Yakk's answer for a more complex, flexible one.
template <template <int> class ToBuild, class Seq, int... Args>
struct build_tuple;
template <template <int> class ToBuild, std::size_t... Idx, int... Args>
struct build_tuple<ToBuild, std::index_sequence<Idx...>, Args...> {
using type = std::tuple<ToBuild<get_n<Idx, Args...>::value>...>;
};
template<int p_first, int p_second, int p_third, int... p_rest>
struct MyClass {
MyClass() {
typename build_tuple<OtherClass,
std::make_index_sequence<sizeof...(p_rest) + 1>,
p_second, p_third, p_rest...>::type middle;
}
};
Use Boost.Hana (requires a C++14 compiler):
#include <boost/hana.hpp>
#include <tuple>
namespace hana = boost::hana;
template <int>
struct OtherClass { };
template <int ...I>
class MyClass {
static constexpr auto ints = hana::tuple_c<int, I...>;
OtherClass<hana::front(ints)> first;
using Middle = typename decltype(
hana::unpack(hana::slice_c<1, sizeof...(I) - 1>(ints), [](auto ...i) {
return hana::type_c<std::tuple<OtherClass<ints[i]>...>>;
})
)::type;
Middle middle;
OtherClass<hana::back(ints)> last;
};
int main() {
MyClass<0, 3, 2, 1> x;
}
Note that I'm abusing a Clang bug in the above, because lambdas are not allowed to appear in unevaluated contexts. To be standards-compliant, you should use a hand-written function object instead of the lambda when calling hana::unpack.
Also, if you go the Hana way, I would suggest that you use hana::tuple if compile-time performance is a consideration, since std::tuple is a slowpoke in all known standard library implementations.
Since the definition of OtherClass is unknown and the vector can't hold different types of OtherClass i assumed following code:
The code uses recursive template inheritance with integer_sequence to split the int pack.
#include <utility>
#include <vector>
template<int...>
struct OtherClass { };
template<typename, int... >
struct Splitter;
template<int... middle, int next, int... rest>
struct Splitter<std::integer_sequence<int, middle...>, next, rest...>
: Splitter<std::integer_sequence<int, middle..., next>, rest...> { };
template<int... middle, int last>
struct Splitter<std::integer_sequence<int, middle...>, last>
{
static std::vector<int> const& get_vector()
{
static std::vector<int> const m = { middle... };
return m;
}
using last_t = std::integral_constant<int,
last
>;
};
template<int p_first, int p_second, int p_third, int... p_rest>
class MyClass
{
OtherClass<p_first> first;
using splitter = Splitter<std::integer_sequence<int>, p_second, p_third, p_rest...>;
std::vector<int> middle = splitter::get_vector();
typename splitter::last_t last;
};
Demo

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());
};

How to implement recursive "using"/extract template parameters

So someone asked a question then deleted it, but I found it an interesting challenge.
They had this weird type, and had run into the problem that Concat and Range were castable to Sequences but were not Sequences (for obvious reasons)
template<unsigned... Is> struct Sequence{};
template<typename... Is> struct Concat{};
template<unsigned... Is> struct Concat<Sequence<Is...>> : public Sequence<Is...> {};
template<unsigned... Is, unsigned... Js, typename... Rest>
struct Concat<Sequence<Is...>, Sequence<Js...>, Rest...>
: Concat<Sequence<Is..., Js...>, Rest...> {};
So I figured it'd be an interesting challenge to do this right, making everything the same type.
So my first pass at Concat ran into unexpected problems...How on earth does one extract the template parameters for the Sequences? I ended up using a function below:
template <unsigned... Is,unsigned... Js>
constexpr auto concat_results(Sequence<Is...>,Sequence<Js...>) -> Sequence<Is...,Js...>
{
return Sequence<Is...,Js...>();
}
template <typename seq1,typename seq2>
using Concat = decltype(concat_results(seq1(),seq2()));
Bizarrely, I think that's the shortest way to declare it, though it is a little weird. However, the concat_results could be useful (on references of the sequences) and the type can be useful in other places. Anyone have a better way to do that?
Next up was range, which I did with a recursive struct helper:
template <unsigned N>
struct nrange_helper
{
using Range = Concat<typename nrange_helper<N-1>::Range,Sequence<N>>;
};
template <>
struct nrange_helper<0>
{
using Range = Sequence<0>;
};
template <unsigned N>
using Range = typename nrange_helper<N>::Range;
Again, its weird, but it works...but I feel like there should be a better way. Anyone got one?
Note: please check out std::integer_sequence, its specialization index_sequence and its make_index_sequence and index_sequence_for facilities.
If you want to declare them yourself, you generally would use inner types:
template <typename, typename> struct Concat;
template <unsigned... Is, unsigned... Js>
struct Concat<Sequence<Is...>,Sequence<Js...>> {
using type = Sequence<Is..., Js...>;
};
template <unsigned N>
struct Range {
using type = typename Concat<typename Range<N-1>::type, Sequence<N>>::type;
};
template <> struct Range<0> { using type = Sequence<0>; };
Which can of course be combined to template aliases for a shorter final form:
template <typename, typename> struct ConcatH;
template <unsigned... Is, unsigned... Js>
struct ConcatH<Sequence<Is...>,Sequence<Js...>> {
using type = Sequence<Is..., Js...>;
};
template <typename L, typename R>
using Concat = typename ConcatH<L, R>::type;
and:
template <unsigned N>
struct RangeH {
using type = Concat<typename RangeH<N-1>::type, Sequence<N>>;
};
template <> struct RangeH<0> { using type = Sequence<0>; };
template <unsigned N>
using Range = typename RangeH<N>::type;
However, indeed, this is slightly verbose. Still, there is little cruft here:
baseline declaration
specialization (to end recursion or limit to "interesting" types)
alias declaration
on the other hand it's only written once.