Declare "container" object from templated template class and variadic templates - c++

I need to declare a class which could store different kind of containers. i.e. It would be nice if it could handle std::bitset and std::array.
However, these two classes need a different of template arguments...
Is it possible (and possibly, how) to use templated template classes and variadic templates to declare this kind of class?
Example (but wrong):
template<template <typename..., std::size_t> class Container,
std::size_t N,
typename... Args>
class Base_Class
{
...
Container<Args..., N/2> container;
};
The compiler complains that N/2 is not a type. Obviously, for both std::array and std::bitset I need the size to be the last template parameter... Is it possible to code this crazyness?
Thank you!
EDIT:
As far as I am concerned, the main problem is that variadic templates can only be expanded on the right, therefore the variadic parameter must be the last one. Anyone know if there are any plans to allow the following syntax in C++17?
template<typename... Args, typename T>
struct A
{};

Anton's answer can be made somewhat less container-specific by using template template parameters for the speciliasations of ResizedContainer:
namespace detail {
template<typename Container>
struct ResizedContainer;
template<template<typename,std::size_t> class Container,
typename T, std::size_t N>
struct ResizedContainer<Container<T,N>> {
using type = Container<T,N/2>;
};
template<template<std::size_t> class Container,
std::size_t N>
struct ResizedContainer<Container<N>> {
using type = Container<N/2>;
};
}
#include <array>
#include <bitset>
template<typename Container>
class Base_Class {
typename detail::ResizedContainer<Container>::type container;
};
int main() {
Base_Class<std::array<int,4>> a;
Base_Class<std::bitset<5>> b;
}

Maybe something like this:
namespace detail {
template<typename Container>
struct ResizedContainer;
template<typename T, size_t N>
struct ResizedContainer<std::array<T, N>> {
using type = std::array<T, N/2>;
};
template<size_t N>
struct ResizedContainer<std::bitset<N>> {
using type = std::bitset<N/2>;
};
}
template<typename Container>
class Base_Class {
typename detail::ResizedContainer<Container>::type container;
};
int main() {
Base_Class<std::array<int, 4>> a;
Base_Class<std::bitset<5>> b;
}

Related

How to template on a container type

I have two functions
template <class T> void foo(std::vector<T>* p)
and
template <class T> void foo(std::valarray<T>* p)
The code in the function bodies is identical. Is there a clever way of writing the template so I can avoid the duplication? Something like
template <class T, class Container> void foo(Container<T>* p)
I'm using C++14.
I only want to allow Container to be either std::vector or std::valarray.
You can also create type traits only for your case.
template <typename T>
struct is_vector_or_valarray : std::false_type {};
template<class T>
struct is_vector_or_valarray<std::vector<T>> : std::true_type {};
template<class T>
struct is_vector_or_valarray<std::valarray<T>> : std::true_type {};
template<typename C, typename = std::enable_if_t<is_vector_or_valarray<C>::value>>
void foo(C* p) {}
Demo
If you want to allow classes derived from std::vector and std::valarray passed in.
template<template<typename...> typename T, typename U>
struct is_tbase_of
{
private:
template<class V>
static std::pair<V, decltype(static_cast<const T<V>&>(std::declval<U>()), std::true_type{})> test(const T<V>&);
static std::pair<U, std::false_type> test(...);
public:
using _aux_type = decltype(test(std::declval<U>()));
using base_type = T<typename _aux_type::first_type>;
static constexpr bool value = _aux_type::second_type::value;
};
template<typename C,
typename = std::enable_if_t<
is_tbase_of<std::vector, C>::value
|| is_tbase_of<std::valarray, C>::value>>
void foo(C* p) {
using base_t = std::conditional_t<
is_tbase_of<std::vector, C>::value,
typename is_tbase_of<std::vector, C>::base_type,
typename is_tbase_of<std::valarray, C>::base_type>;
auto pbase = static_cast<base_t*>(p);
// operate on pbase
}
The type traits for checking if a class inherits from a template class comes from this answer. I make some subtle changes based on it(mainly for getting the base type).
As #Raymond says, c should be cast to its base pointer to operate on the sliced part. I believe it should be done in the function body, otherwise, there'll be a non-deduced context.
Demo
Note: you cannot really specialize on std::vector<> and std::valarray<>, but you can say, for the given (deduced) T, you expect std::vector<T> or std::valarray<T>. Thus aliases will also be resolved.
The trick is, you templetize for a template class C and a type T (and possibly further parameters, as in std), then use std::enable_if<> that only resolves (to void in this case) when either std::vector<T> or std::valarray<T> was resolved.
Also note, you might want to allow custom allocators et. al., i.e., instead of a single T, it's recommended to use typename... Ts for the container.
#include <iostream>
#include <vector>
#include <valarray>
#include <type_traits>
template<template<class...> class C, typename T>
auto foo(C<T>* arg) -> std::enable_if_t<std::is_same<C<T>, std::vector<T>>::value || std::is_same<C<T>, std::valarray<T>>::value>
{
}
int main() {
std::vector<int> v;
std::valarray<int> va;
//std::pair<int, int> p; // this should fail
foo(&v);
foo(&va);
//foo(&p); // this fails as expected
return 0;
}
If you also want to handle derived classes, you might use std::is_base_of<> - but as for classes in std namespace, it's not recommended to derive from them.

C++ template inheritance 2 arguments

is it possible to inherit everything from template class and just rewrite some of it's functions specialized to int, double or float?
Is there a way to write something similar to this?
template<typename T, size_t N>
class Container<int, N> : public Container<T, N> {
};
No, you need to use a different name.
template<class T, size_t N>
struct ContainerBase:std::array<T,N> {
// some methods here
};
template<class T, size_t N>
struct Container:ContainerBase<T,N> {
// inherit any constructors:
using ContainerBase<T,N>::ContainerBase;
};
template<size_t N>
struct Container<int, N>:ContainerBase<int,N> {
using ContainerBase<int,N>::ContainerBase;
// overload (not override) methods here
};

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

Simplify template arguments

I need to create a template class depending on a simple struct like this. It has a method AddNew to insert new element for its tuple member.
struct Type
{
int i;
char c;
};
template<typename ...T>
class A
{
std::tuple<T...> ts;
public:
A(T&&...t):ts(std::move(t)...){}
void AddNew(T t)
{
ts=std::tuple_cat(ts,t);
}
};
int main()
{
A<Type,Type,Type,Type> a({1,'a'},{2,'t'},{3,'c'},{4,'g'});
Type t{5,'x'};
a.AddNew(t);
}
Problem I don't want to write down Type... 4 times like in main. I like something like A<Type,4> when I initialize A with 4 Types or A<Type, 100> for 100 Types.
You probably just want vector:
struct Type
{
int i;
char c;
};
template<typename T>
class A
{
std::vector<T> data;
public:
template <typename ... Ts>
A(Ts&&...ts) : data(std::forward<Ts>(ts)...){}
void AddNew(const T& t)
{
data.push_back(t);
}
};
int main()
{
A<Type> a(Type{1,'a'}, Type{2,'t'}, Type{3,'c'}, Type{4,'g'});
Type t{5,'x'};
a.AddNew(t);
}
you may partially specialize A for some helper 'tag' type:
template<typename T, std::size_t N>
struct repeat{};
template<typename... T>
struct repeat_unpack{ using type = A<T...>; };
template<typename T, int N, typename... V >
struct repeat_unpack<repeat<T,N>,V...>: std::conditional_t<(N>1),
repeat_unpack<repeat<T,N-1>,T,V...>,
repeat_unpack<T,V...>
>{};
template<typename T, int N>
class A<repeat<T,N>>: repeat_unpack<repeat<T,N>>::type {};
// to be used as
A<repeat<Type,3>>
the above uses inheritance to avoid code duplication, so you'll probably need to lift some members (eg. constructors) to the partial specialization as well.
Otherwise, just use an alias
template<class T, std::size_t N>
using A_nth = typename repeat_unpack<repeat<T,N>>::type;
A_nth<Type,3>
Just one more thing, given that you're using tuple internally, it should be easy to simply specialize for std::array and use it instead, being tuple compatible ...
PS. obviously, your AddNew() code makes no sense as is, I'd assume it's just a code snippet typo ...

How can I use `std::array` for a template parameter of the form `template<typename> class`?

Please consider the following tree class
template<typename T, template<typename> class Tuple>
class tree
{
private:
T m_value;
Tuple<tree> m_children;
};
template<typename T, std::size_t N>
using static_tree = tree<T, std::array<T, N>>;
which is not well-defined. std::array<T, N> is not a suitable template parameter for Tuple. I assume the intend of static_tree is clear. We could do something like
template<std::size_t N>
struct helper
{
template<typename T>
using type = std::array<T, N>;
};
template<typename T, std::size_t N>
using static_tree = tree<T, helper<N>::template type>;
Is there any other option without the helper class?
Rather than having the helper function be an exception for std::array, I'd propose that be the rule. Instead of taking a template template parameter, take a metafunction class parameter. Template metaprogramming is a lot easier when everything everywhere is a type (avoiding template templates and non-type arguments):
template<typename T, typename TupleMfn>
class tree
{
private:
using Tuple = TupleMfn::template apply<tree>;
T m_value;
Tuple m_children;
};
with:
template <size_t N>
struct array_builder {
template <class T>
using apply = std::array<T, N>;
};
template <typename T, size_t N>
using static_tree = tree<T, array_builder<N>>;
This will make it easier to use with other kinds of containers as well, since we can make a wrapper for template templates that gives us back a metafunction:
template <template <typename...> class X>
struct as_metafunction {
template <class... Args>
using apply = X<Args...>;
};
template <typename T>
using vector_tree = tree<T, as_metafunction<std::vector>>;
If you're feeling especially feisty, you can provide a metaprogramming-friendly version of std::array:
template <class T, class N>
struct meta_array : std::array<T, N::value> // obviously I am terrible at naming things
{ };
template <size_t N>
using size_t_ = std::integral_constant<size_t, N>;
And then provide placeholder args for tree to apply:
template <class T, size_t N>
using static_tree = tree<T, meta_array<_, size_t_<N>>>;
template <class T>
using vector_tree = tree<T, std::vector<_>>;
I think your question contains a fundamental problem which is different from what you were explicitly asking (it threw me off a bit in the previous iteration of this answer). Combining the different parts of your question, you're essentially trying to instantiate some tree class that has a member that is an std::array of the same class as well. This is obviously impossible. You probably want that the tree should hold some Tuple of pointers (smart or otherwise).
One way to do so would be to use your helper class, but modifying the class to
template<typename T, template<typename> class Tuple>
class tree
{
// Indirection (I'm omitting the question of whether these should be
// smart pointers.
Tuple<tree<T, Tuple> *> m_children;
};
A different way would make Tuple a regular template parameter, as follows:
#include <array>
#include <type_traits>
template<typename T, class Tuple>
class tree
{
private:
static_assert(
std::is_same<void *, typename Tuple::value_type>::value,
"Tuple must be a container of void *");
private:
T m_value;
Tuple m_children;
};
template<typename T, std::size_t N>
using static_tree = tree<T, std::array<void *, N>>;
int main()
{
static_tree<int, 8> t;
}
On the one hand, the helper class has been eliminated. OTOH, Tuple is a container of void *: the instantiators are aware of this, and the class internally needs to perform casts. It's a tradeoff. I would stick to your original version (with the modifications suggested, of course).
A class X cannot contain multiple copies of actual instances of class X, except logically.
An if we have
struct X {
std::array<X, 2> data;
};
the only possible size for X is infinity, as sizeof(X) = 2*sizeof(X), and all types in C++ have sizeof(X)>=1.
C++ does not support infinitely large types.
Your second problem is that type instances are not templates.
template<typename T, template<typename> class Tuple>
class tree
this takes a type T and a template Tuple. The second argument is not a type.
template<typename T, std::size_t N>
using static_tree = tree<T, std::array<T, N>>;
here, your second argument is a type, not a template.
template<std::size_t N>
struct array_of_size {
template<class T>
using result=std::array<T,N>;
};
template<typename T, std::size_t N>
using static_tree = tree<T, array_of_size<N>::template result>;
would, other than the above "infinite size problem", solve your problem. Here we are passing the template array_of_size<N>::result to tree.
To solve the infinite size problem, you must store pointers (or something equivalent) in the array. So we get:
template<std::size_t N>
struct array_of_ups_of_size {
template<class T>
using result=std::array<std::unique_ptr<T>,N>;
};
template<typename T, std::size_t N>
using static_tree = tree<T, array_of_ups_of_size<N>::template result>;
and now your static_tree has N children, each of which is a unique_ptr to a similar static_tree.
This still doesn't work, because of destructor issues.
template<typename T, template<typename> class Tuple>
class tree
{
private:
T m_value;
Tuple<tree> m_children;
public:
~tree();
};
template<typename T, template<typename> class Tuple>
tree<T,Tuple>::~tree() = default;
I think the above fixes it, strange as it may seem.
Basically, when you make the array of children, the tree type is incomplete. At destruction, delete is called. At that point, tree must be complete. By deferring the dtor, we hopefully deal with the problem.
I am uncertain if this technique is required for templates, but it is for non-template classes.
Or you could implement a template parameter bind as you suggested (slightly more general than your helper):
template<std::size_t N, template<typename,std::size_t> class T>
struct bind2nd
{
template<typename F>
using type = T<F,N>;
};
template<std::size_t N, typename T>
using static_tree = tree<T, bind2nd<N,std::array>::template type>;