define a class `MultiInherit<MyTuple<X1,X2,...>>` to inherit from `X1,X2,...` - c++

How to define a class MultiInherit<MyTuple<X1,X2,...>> to inherit from X1,X2,... ?
I want to pass MyTuple<X1,X2,...> around to compose a new object type.
template <class... Xs> class MyTuple{};
template <class MyTupleXs> class MultiInherit : public All_Xs_in_MyTupleXs { //??
};
Here is its usage and MCVE (uncompilable):-
class B{public: int b;};
class C{public: int c;};
class BC : public MultiInherit<MyTuple<B,C>> {
public: void testCompile(){
this->b=this->c;
}
};
int main(){
}
Attempt 1
Below is the closest to what I wanted.
If I get raw-types (e.g. B,C) as template argumant rather than MyTuple<B,C>, it would be easy :-
(MCVE, compilable)
template <class... Xs> class MultiInherit : public Xs... {
};
class B{public: int b;};
class C{public: int c;};
class BC : public MultiInherit<B,C> {
public: void testCompile(){
this->b=this->c;
}
};
Reference: Variadic templates and multiple inheritance in c++11
This doesn't solve. I want to pass an encapsulated type MyTuple<B,C>, not the B,C.
Attempt 2
I thought about adding a new variadic typedef MyXs in MyTuple:-
template <class... Xs> class MyTuple{
public: using MyXs=Xs...;
};
template <class MyTupleXs> class MultiInherit : public MyTupleXs::MyXs {
};
Nevertheless, it seems to me that the language has no such feature.
(I am new to variadic template.)

Simple answer
You have to create a template specialization of MultiInherit. Let's look at the base case of MultiInherit:
template<class... Xs>
class MultiInherit : public Xs... {
// Stuff
};
Here, we take a bunch of types and inherit from them. But we can use a template specialization to unpack tuples:
// Specialization on MyTuple
template<class... Xs>
class MultiInherit<MyTuple<Xs...>> : public Xs... {
// Stuff
};
This allows you to do what you want. Given a MyTuple, you have a MultiInherit class that inherits from all the members.
More general case
Defining a type list. If you want to do things more generally, it'll be useful to do them based on templates representing type lists. We can define a template to represent a type list:
template<class...>
struct TypeList {};
Concatenating type lists. We can define more templates to concatenate type lists. ConcatLists is a function that takes two type lists, and returns a TypeList that concatenates them:
template<class... Xs, class... Ys>
TypeList<Xs..., Ys...> ConcatLists(TypeList<Xs...>, TypeList<Ys...>) {
return {};
}
We can use that to define a concat_t that does that automatically:
template<class TypeListA, class TypeListB>
using concat_t = decltype(ConcatLists(TypeListA{}, TypeListB{}));
Substituting MultiInherit for TypeList. We can also substitute one type for another type using template specializations.
// Base case
template<template<class...> class Replacement, class List>
struct SubstituteList;
template<
template<class...> class Replacement,
template<class...> class Original,
class... Xs>
struct SubstituteList<Replacement, Original<Xs...>> {
using type = Replacement<Xs...>;
};
template<template<class...> class Replacement, class List>
using substitute_list_t = typename SubstituteList<Replacement, List>::type;
This means that you can take a TypeList and replace it with a MultiInherit:
template<class ListOfTypes>
using MultiInheritFromTypeList = substitute_list_t<MultiInherit, ListOfTypes>;
Example usage. If you want to convert a TypeList to a MultiInherit:
using T1 = TypeList<int, char, bool>;
// T2 is MultiInherit<int, char, bool>;
using T2 = MultiInheritFromTypeList<T1>;
Example with concat. Or, if you want to concat multiple TypeLists and convert it to a MultiInherit:
using T1 = TypeList<int, char, bool>;
using T2 = TypeList<double, float>;
// T3 is MuliInherit<int, char, bool, double, float>
using T3 = MultiInheritFromTypeList<concat_t<T1, T2>>;

Related

Class templates with multiple unrelated arguments

I have a class template, that creates a class with two members:
template<typename coordinateType, typename ...DataTypes>
class Object{
public:
std::tuple<coordinateType, coordinateType, coordinateType> position;
std::tuple<std::vector<DataTypes>...> plantData;
};
The issue is, rather than calling
auto myObject = Object<float, int, int, int>();
for an instance of Object with 3 ints of data, I want to clean this up and use two separate templates, without the unrelated "float" as the first argument.
Is there a way to implement this class so that it would be the equivalent of:
auto myObject = Object<float>();
myObject.track<int, int, int>();
And if not, is it possible to separate those two template arguments in any way, or am I stuck with grouping them together?
if you change the caller side a little, you can make track to return a new class.
template<typename T, typename...Us>
struct Object{
std::tuple<T, T, T> position;
std::tuple<std::vector<Us>...> plantData;
};
// you can also give it a different name, here I use a specialization instead
template<typename T>
struct Object<T>{
template<typename...Us>
Object<T,Us...> track();
};
void foo(){
auto myObjectCreator = Object<float>();
auto myObject = myObjectCreator.track<int, int, int>();
}
https://godbolt.org/z/qhPrEaa96
If I understand you correctly, you'd like track<Ts> to append Ts to the list of DataTypes. This can be achieved like:
template<typename coordinateType, typename ...DataTypes>
class Object{
public:
template<typename... Ts>
Object<coordinateType, ...DataTypes, Ts>
track() { return {}; } // you might add proper ctor call, element copy via tuple::cat, etc.
std::tuple<coordinateType, coordinateType, coordinateType> position;
std::tuple<std::vector<DataTypes>...> plantData;
};

Construct a type list in compile-time, one at a time

I have a type list that I've been using but I want to make it cleaner.
My current implementation relies on all the types to be listed when declaring the type list.
template<typename... Types>
struct TypeList {};
using MyCustomTypeList = TypeList<Type1, Type2, Type3>; // Currently listed in one place. This is what I want to avoid.
I would like to build a type list is such a way where I could just write: ADD_TYPE(type) and it would add to MyCustomTypeList.
One of the requirements is that a function call whose signature looks like
template<typename... Types>
void MyFunction(TypeList<Types...>&&);
is still callable with MyFunction( MyCustomTypeList{} );
C++ 17 is available; boost or other libraries aren't.
If you want to update your list like so:
using list1 = pack<int>;
using list2 = push_back_t<list1, char>; // pack<int, char>
using list3 = push_back_t<list2, double>; // pack<int, char, double>
We can write push_back like this:
template <class T>
struct tag
{
using type = T;
};
template <class T>
using detag = typename T::type;
////////////////////////////////
template <class, class>
struct push_back {};
template <template <class...> class C, class ... Ts, class NewT>
struct push_back <C<Ts...>, NewT> : tag<C<Ts..., NewT>> {};
template <class List, class NewT>
using push_back_t = detag<push_back<List, NewT>>;
Demo
Note: I renamed TypeList to pack, as lowercase is standard for type traits.

inherit type_trait of Derived in CRTP : type of Derived is not complete (yet)

Library code
My library has a CRTP class B<Derived>.
I created a Trait<T> class to enable user to change behavior of B.
The default setting is int. (#1)
#include <iostream>
#include <string>
//B and Trait (library class)
template<class Derived> class B;
template<class T>class Trait{
public: using type = int; //<-- default setting //#1
};
template<class Derived> class B{
public: using type = typename Trait<Derived>::type; //#2
public: type f(){return 1;}
};
User code ( full coliru demo )
Then, I create a new class C with a new setting std::string. (#3)
It works fine.
//C (user1's class)
template<class Derived> class C ;
template<class Derived>class Trait<C<Derived>>{
public: using type = std::string; //#3
};
template<class Derived> class C : public B<Derived>{};
Finally, I create a new class D.
I want D to derive C's setting i.e. std::string (not int).
However, it is not compilable at $.
//D (user2's class)
class D : public C<D>{ //#4
public: type f(){return "OK";} //#5
//$ invalid conversion from 'const char*' to 'B<D>::type {aka int}'
};
int main(){
D dt;
std::cout<< dt.f() <<std::endl;
}
My understanding
Roughly speaking, here is my understanding about the compile process :-
Just before class D (#4), it doesn't know about D.
At #4, to identity D::type, it looks up C<D>::type.
Finally, it finds that it is defined in B<D>::type at #2.
From #2, it travels to the definition at #1 and find type = int.
Thus D::type = int.
Note that #3 is ignored, because at this point (#4 and #5), D is still incomplete.
The compiler still doesn't fully recognize that D is derived from C<something> ... yet.
Question
How to let D automatically inherit Trait's setting from C without explicitly define another template specialization Trait<D>?
In other words, how to make #3 not ignored for D?
Trait is probably not a good design (?), but I prefer to let the type setting be in a separate trait class.
The instantiating goes like this:
D -> C<D> -> B<D> -> Traits<D>
Traits<D> does not match you partial specialization of Traits<C<Derived>>
If you change it to template<class Derived> class C : public B<C<Derived>>{}; that will in turn instantiate Traits<C<D>> and that will match your specialization and you get std::string as type.
To get the child from B you can use.
template <typename... T>
struct getChild;
template <template <typename... T> typename First, typename... Rest>
struct getChild<First<Rest...>> { using child = typename getChild<Rest...>::child; };
template <typename First>
struct getChild<First> { using child = First; };
and then add in
template<class Derived> class B{
public: using type = typename Trait<Derived>::type;
using child = typename getChild<Derived>::child;
public: type f(){return 1;}
};

Enable if/else class member template instantiation

Can anyone tell my how to enable if/else class member template based on different derived classes from pre-defined base set? Let me use the following example:
enum class Type {
TYPEA,
TYPEB
};
// Predefined in libraries.
class BaseA {...};
class BaseB {...};
class Foo {
template <typename Derived, Type type>
void foo();
};
// User-derived
class DerivedA : public BaseA {};
class DerivedB : public BaseB {};
Normally we need two template typenames for calling the member foo.
Foo obj;
obj.foo<DerivedA, Type::TypeA>()
obj.foo<DerivedB, Type::TypeB>();
However, this native approach seems lengthy because the second template argument Type::TypeA and Type::TypeB can obviously be deduced by compiler through the first argument DerivedA and DerivedB, if they are derived from pre-defined base properly. I notice that c++11 provides is_base_of template but I am not sure how to use it in my case. To be more specific, below is the expected solution:
obj.foo<DerivedA>(); // Automatically deduce type = Type::TypeA
obj.foo<DerivedB>(); // Automatically deduce type = Type::TypeB
And if the compile fails to deduce the Type from the first typename, it should it just goes back to the normal declaration obj.foo<MyClass, MyType> where MyType is either Type::TypeA or Type::TypeB.
Sounds like you just want a default template argument:
class Foo {
template <typename Derived, Type type = get_type_from<Derived>::value>
void foo();
};
Where get_type_from<> is a metafunction to be filled in later based on how you actually figure out the Types.
template<Type t>
using etype_tag = std::integral_constant<Type, t>;
template<class T>
struct tag_t {
using type=T;
template<class D,
std::enable_if_t<std::is_base_of<T, D>::value, int>* =nullptr
>
constexpr tag_t( tag_t<D> ) {}
constexpr tag_t() = default;
constexpr tag_t(tag_t const&) = default;
};
template<class T>
constexpr tag_t<T> tag{};
constexpr etype_tag<Type::TYPEA> get_etype( tag_t<BaseA> ) { return {}; }
constexpr etype_tag<Type::TYPEB> get_etype( tag_t<BaseB> ) { return {}; }
template<class T>
constexpr decltype( get_etype( tag<T> ) ) etype{};
Now etype<Bob> is a compile-time constant integral constant you want.
class Foo {
template <typename Derived, Type type=etype<Derived>>
void foo();
};
makes the 2nd argument (usually) redundant.
You can extend get_etype with more overloads in either the namespace where etype is declared, or in the namespace of tag_t, or in the namespace of the type you are extending get_etype to work with, and etype will automatically gain support (assuming it is used in a context where the extension is visible: failure of that requirement leaves your program ill formed).
Live example

expanding parameter pack in templated constructor of templated class

Before I describe the problem I will give you an idea what is the target of my work.
I want to have a template which creates a class ( while do this unrolling a typelist recursively) which derives from all given types in a variadic list of parameters. That works fine. (see below)
Now is my target to provide all parameters to all constructors of the subclasses via an "automatic" created type from the unrolling template. Finally each of the Unroll classes should eat the parameters it needs to create a instance of the given classes. Each of the recursively created template instances should eat one of the parameter packs contained in the TypeContainer.
Before you ask: This code is only academic to learn new features of c++11. :-)
// create a wrapper around tuple to make it constructible with initializer list
template <typename ... T>
class TypeContainer: std::tuple<T...>
{
public:
TypeContainer(T... args):std::tuple<T...>(args...){};
};
// create a template to concatenate some typelists
// ??? is there a already usable template in std:: ???
template < typename ... X >
class TypeConcatenate;
template <typename T, typename ... S >
class TypeConcatenate < T, TypeContainer< S... >>
{
public:
typedef TypeContainer< T, S...> type;
};
// The follwing template unrolls a typelist and creates a recursively
// inherited class.
template <typename ... T> class Unroll;
template < class Base, class Head, class ... Next >
class Unroll< Base, Head, Next...>: public Unroll < Base, Next...>
{
public:
// collect all needed types for the instance creation of all child
// classes.
typedef typename TypeConcatenate<typename Head::Parms, typename Unroll < Base, Next...>::AllParms>::type AllParms;
};
template < class Base, class Head>
class Unroll < Base, Head>
{
// provide first parameter set for the constructor
public:
typedef TypeContainer<typename Head::Parms> AllParms;
};
template < class Base, class ... Next>
class Top : public Unroll < Base, Next...>
{
// I want to have a constructor which accepts
// all parameters for all the sub classes.
public:
template <typename ...T> Top(T... args);
};
// ??? The following lines of code will not compile!!!
// gcc 4.8.1 gives:
// error: ISO C++ forbids declaration of 'Top' with no type
// ??? Why the compiler could not interpret this as constructor ???
template <typename Base, typename ... Next, typename ... T>
Top<Base, Next...>::Top< TypeContainer<T...>>( T... args) {}
class Base {};
class A: public Base
{
public:
typedef TypeContainer<int, float> Parms;
A( int i, float f){}
} ;
class B: public Base
{
public:
typedef TypeContainer< char, int> Parms;
B( char c, int i){}
};
Top<Base, A, B> top {A{ 1,1},B{1,1}};
Questions:
1) Is there maybe a simpler way to determine the parameter list for the class
hierarchy. My way with typedef typename TypeConcatenate<typename Head::Parms, typename Unroll < Base, Next...>::AllParms>::type AllParms; looks a bit hard :-)
2) Because of 1) I have a kind of type container which holds T... and the problem with my constructor arise with the unpacking of the parameter list which is contained in a container. Maybe my solution is much to complex. Nay hint to solve the basic idea?
3) Ignoring that the problems of 1) and 2) are comes from a totally boring design I want to know which I couldn’t specialize the constructor with
template <typename Base, typename ... Next, typename ... T>
Top<Base, Next...>::Top< TypeContainer<T...>>( T... args) {}
For any further discussion: Yes, I know that the parameters should be forwarded and not given as value an so on. But I want to simplify the example which seems long enough for the discussion.
It sounds like you want a variadic forwarding constructor, something like:
template <typename... Ts>
class lots_of_parents : public Ts...
{
public:
template <typename... Args>
lots_of_parents(Args&&... args) : Ts(std::forward<Args>(args))... {}
};
lots_of_parents<A, B> mytop {A{1, 1}, B{1, 1}};
and either (a) the rest of the question is XY problem, or (b) I'm simply not understanding.
EDIT: Missed the question about your constructor. Ignoring for the moment that functions cannot be partially specialized, I think the syntax would look like:
template <typename Base, typename ... Next>
template <typename ... T>
Top<Base, Next...>::Top< TypeContainer<T...>>( T... args) {}
Which still doesn't really make sense, there's no way to deduce TypeContainer<T...> from T... args.