How to inherit from a list of types and then call a member on the list of inherited members?
Asked Answered
A

4

9

I have a set of classes that have the following structure:

class U
{
public:
    explicit U(int) { ... }
    U() {...}
    Init(int) {...}
};

I need to be able to compose 1 or more of these classes into a class X. Pseudocode:

template<class TypeSequence>
class X that derives publicly from all the classes in TypeSequence
{
    X(int): all bases are initialized with the integer passed 
    {}
    //if the above constructor is impossible, then the following will do as well:
    X(int)
    {
        Call Init on all bases and pass the given int to them.
    }
};

I think I need a lot of mpl, but I'm not really good at it. Is what am I trying to do doable? A code sample would be great.

MY FAULT: Forgot to mention I can't use C++11 features. I am looking for an MPL solution.

Ablate answered 8/9, 2011 at 17:56 Comment(8)
+1 Good question. Have you heard of mixin by the way? Maybe, it can be applicable here, or a variant of it?Obnoxious
I've done exactly this before with Boost.MPL, but I don't have the code handy and I don't have time to write a full answer right now. I'll revisit this tonight if no one has answered by then. As a hint, I recall making a special mixin that takes two boost::mpl::vector<> iterators as template arguments.Macaroon
How is U related to TypeSequence?Suddenly
@Nawaz: I don't think mixin is applicable. I don't need info about the derived class in U classes. I need something along the lines of mpl::inherit_linearly, I just can't figure out the syntax. Also I have no idea how I could initialize the bases or call Init on themAblate
Definitely needs the variadic template feature of C++0x.Imperturbable
@Captain: U isn't a single class. There are a lot of classes like U. The TypeSequence is a sequence of such typesAblate
@ildjarn: Looking forward to it :)Ablate
@Frigo: I'm pretty sure MPL will doAblate
H
6

Well, Boost.MPL contains metafunctions inherit and inherit_linearly you can combine them with for_each to get the second variant (with init functions). Or using just boost::mpl::fold and custom metafunctions:

struct Null_IntConstructor
{
  Null_IntConstructor(int) { }
};

struct InheritFrom_IntConstructor_Folder
{
  template<typename T1, typename T2>
  struct apply
  {
    struct type : T1, T2
    {
      type(int x) : T1(x), T2(x) { }
    };
  };
};

template<typename Bases>
struct InheritFrom_IntConstructor 
  : boost::mpl::fold<Bases, 
                     Null_IntConstructor,
                     InheritFrom_IntConstructor_Folder>::type
{
  InheritFrom_IntConstructor(int x) 
    : boost::mpl::fold<Bases, 
                       Null_IntConstructor,
                       InheritFrom_IntConstructor_Folder>::type(x) 
  { }
};

Usage example:

#include <iostream>
#include <boost/mpl/fold.hpp>
#include <boost/mpl/vector.hpp>

struct A
{
  A(int x) { std::cout << "A::A " << x << std::endl; }
};

struct B
{
  B(int x) { std::cout << "B::B " << x << std::endl; }
};

struct C
{
  C(int x) { std::cout << "C::C " << x << std::endl; }
};

int main()
{
  InheritFrom_IntConstructor< boost::mpl::vector<A, B, C> >(1);
}

Metafunction InheritFrom_IntConstructor can generalized to accept arbitrary type as constructor parameter and I'm not sure if can generalized to accept arbitrary number of arguments.

Heredes answered 8/9, 2011 at 18:17 Comment(2)
@Begeoth: Works perfectly. I need to look into what exactly fold does, though, but thank you very very much!.Ablate
@Armen: mpl::fold does almost exactly what I did in my solution.Obnoxious
H
4

Something like this?

template <typename ...BaseClasses>
class Aggregator : public BaseClasses...
{
public:
    Aggregator(int i) : BaseClasses(i)...
    {}

};

Usage example:

Aggregator<U, V, W> a(10);
Aggregator<U, V> b(15);
Aggregator<W> c(20);

Note: it uses variadic templates, so C++11 is required.

Hollands answered 8/9, 2011 at 18:9 Comment(4)
+1: This is soooo clean and simple. Thank you. Unfortunately, UNFORTUNATELY I cannot use variadic templates :(Ablate
BaseClasses(i)... That works? § 14.5.3/5 seems to read that it does! Neat!Kinase
Nice example of C++11 expressiveness. I want to generalize the original question to arbitrary number of arbitrary types constructor arguments.Heredes
@Mooing Duck: actually it's a completely new argument to me, I didn't really check the standard, g++ accepted it and I assumed it was correct. :pHollands
O
2

I'm not using Boost, and I'm not sure how much my solution is closer to what you need. But still I'm posting it:

template<typename typeseq>
struct X : typeseq::head, X<typename typeseq::tail>
{
   typedef typename typeseq::head base;
   typedef X<typename typeseq::tail> recursebase;
   X(int i) : base(i), recursebase(i) {}
   void init(int i) 
   {
       base::init(i);
       recursebase::init(i);
   }
};

template<>
struct X<null_type> 
{
   X(int i) {}
   void init(int i)  { } 
};

And then, testing code:

typedef typelist<S,typelist<U>> typeseq;
X<typeseq> x(10);
x.init(100);

Online demo : http://ideone.com/e6tuM

Obnoxious answered 8/9, 2011 at 18:25 Comment(1)
When I saw Begemoth's answer, this occurred to me too and I was about to post it. You were faster though :D Way better answer than my originals.Kinase
K
1

Something like this should work:

template<class typeOne>
class X1 : public typeOne
{
    X(int b): typeOne(b)
    {}
};
template<class typeOne, class typeTwo>
class X2 : public typeOne, public typeTwo
{
    X(int b): typeOne(b), typeTwo(b)
    {}
};
template<class typeOne, class typeTwo, class TypeThree>
class X3 : public typeOne, public typeTwo, public typeThree
{
    X(int b): typeOne(b), typeTwo(b), typeThree(b)
    {}
};

or, if you're willing to waste a few bytes per object, you can use a placeholder, and only make a large one. This should waste at most one byte per unused base type per instance.

template<int>
class PlaceHolder { PlaceHolder(int){} };

template<
         class typeOne, 
         class typeTwo=PlaceHolder<2>, 
         class TypeThree=PlaceHolder<3>,
         class TypeFour=PlaceHolder<4>,
         class TypeFive=PlaceHolder<5>
        >
class X : 
         public typeOne, 
         public typeTwo, 
         public typeThree,
         public typeFour,
         public typeFive
{
    X(int b)
    : typeOne(b), 
    typeTwo(b), 
    typeThree(b),
    typeFour(b),
    typeFive(b)
    {}

    X(const X& b)
    : typeOne(b), 
    typeTwo(b), 
    typeThree(b),
    typeFour(b),
    typeFive(b)
    {}

    X& operator=(const X& b) {
        typeOne::operator=(b);
        typeTwo::operator=(b);
        typeThree::operator=(b);
        typeFour::operator=(b);
        typeFive::operator=(b);}
        return *this;
    }
};
Kinase answered 8/9, 2011 at 18:0 Comment(4)
You cannot have the same template with different template argument numbersAblate
alright, changed the template names, and added a version that works with only one name, but may have wasted space.Kinase
I honestly don't like your solution, but I am upvoting it because you spent time to try to help me. ThanksAblate
Honestly, I don't like my solution either. :DKinase

© 2022 - 2024 — McMap. All rights reserved.