Tuple of sequence - c++

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

Related

Template-defined number of template parameters (very meta)

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

Is there a way to count member variable of a class in compile time?

What I want to do is to check member variable count of a class, as some newbie might write too much member variable to read. Is there some way to get it?
We did do code reviews, but a static_assert(GET_MEM_VAR_COUNT(ClassA) < 10) might be more straight and clear.
Until we get reflection, you are stuck using another tool to check the number of members in a class.
We have a few crude ways to get reflection now, with many limitations. If you only have a data struct, then you can use Boost Fusion to define your class such that you can assert on its size, for example:
#include <string>
#include <boost/fusion/include/define_struct.hpp>
#include "boost/mpl/size.hpp"
BOOST_FUSION_DEFINE_STRUCT(
(my_namespace), my_class,
(std::string, member1)
(int, member2)
(int, member3)
(double, member4)
//Uncomment me to trigger assert (double, member5)
)
static_assert(boost::mpl::size<my_namespace::my_class>::type::value < 5, "Classes must have fewer than 5 members");
Demo
There is a way for aggregate types.
Example:
#include <iostream>
#include <utility>
namespace impl {
struct any_type {
template<typename T>
constexpr operator T();
};
template <class T, class... Ts>
decltype(void(T{ { std::declval<Ts>() }... }), std::true_type{}) test_is_braces_constructible(std::size_t);
template <class, class...>
std::false_type test_is_braces_constructible(...);
template<std::size_t N, class T, class... Ts>
struct sizeof_struct : public std::conditional_t<
decltype(test_is_braces_constructible<T, Ts...>(0))::value,
sizeof_struct<N + 1, T, any_type, Ts...>,
std::integral_constant<std::size_t, N>
> {};
}
template<typename T, typename = std::enable_if_t<std::is_aggregate_v<T>>>
using sizeof_struct = typename impl::sizeof_struct<0, T, impl::any_type>;
template<typename T>
constexpr inline auto sizeof_struct_v = sizeof_struct<T>::value;
struct Foo {
int i;
float f;
double d;
};
int main() {
std::cout << sizeof_struct_v<Foo>; //outputs 3
return 0;
}
This was made for C++17 but can be converted to be used in C++14 and even C++11.

How to fix 'X is not a class template' when dealing with ints in struct templates?

I can't get my code to compile, I'm trying have a class which stores information about types using a struct template which accepts an int and a parameter pack.
#include <tuple>
#include <cassert>
#include <iostream>
#include <cstring>
#include <vector>
template<int N, typename... Ts>
struct type_info_impl<N, Ts...> {
typedef typename std::tuple_element<N, std::tuple<Ts...>>::type type;
static const size_t size = sizeof(type);
};
template<typename... Types>
class type_info {
public:
type_info(){}
~type_info(){}
template<int N>
static constexpr size_t size(){
return type_info_impl<N, Types...>::size;
}
};
using types = type_info<bool, int, double>;
using namespace std;
int main()
{
cout << types::size<1>() << endl;
return 0;
}
The number "4" should be outputted since the size of the type (int) at index 1 is 4 but it instead throws "‘type_info_impl’ is not a class template
struct type_info_impl {"
The syntax you are using to define type_info_impl is wrong.
Use of
template<int N, typename... Ts>
struct type_info_impl<N, Ts...> { ...};
is OK if you are trying to specialize the class template. To define the basic class template, remove the <N, Ts...> bit. Just use
template<int N, typename... Ts>
struct type_info_impl { ...};

Declaring a function with types of arguments taken from a foreign parameter pack

Suppose I have some kind of type list
template<typename... Types> struct TypeList {};
Now in some other class I can generate such a TypeList in a variety of ways.
template<class T> struct MyClass {
using MyList = TypeList<T, typename Something<T>::type, SomethingElse>;
// ...
};
How can I declare a method with types of arguments extracted from this type list? For example, if I set MyList = TypeList<int, float, const char*>, I wish a method
void my_method(int, float, const char*)
to be declared.
You could derive from a base class that implements the method:
template <typename> struct MethodProvider;
template <typename ...Args>
struct MethodProvider<TypeList<Args...>>
{
void my_method(Args ...args);
};
template <typename T>
struct MyClassAux
{
using MyList = TypeList<T, typename Something<T>::type, SomethingElse>;
};
template <typename T>
struct MyClass
: private MyClassAux<T>
, private MethodProvider<typename MyClassAux<T>::MyList>
{
using typename MyClassAux<T>::MyList;
using MethodProvider<typename MyClassAux<T>::MyList>::my_method;
// ...
};
You can use static_assert and std::is_same to constraint the arguments of my_method
#include <iostream>
#include <string>
#include <vector>
#include <type_traits>
template<typename... Types> struct TypeList {};
template<class T> struct MyClass {
using MyList = TypeList<int, T>;
template<typename ...Args>
void my_method(Args ...args) {
static_assert(std::is_same<MyList, TypeList<Args...>>::value, "invalid arguments");
auto dummy = {(std::cout << args << "\n", 0)...};
(void)(dummy); // avoid variable warning
}
};
int main()
{
MyClass<float> c;
c.my_method(1, 2.3f);
// c.my_method(1, ""); // unmatched arguments won't compile
// c.my_method(1);
}
Online Demo

Template type defined array initialisation

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]