Skip to main content
GameDev.net gamedev.net
🔒 Locked

Workaround for virtual function templates in C++

Started by lhead Nov 20, 2006 at 2:22 PM 28 replies 11.6k views
Original Post
lhead
lhead
Hi! I found a workaround for the problem of virtual template methods in C++. I would like to get some technical feedback ! Especially about the performance. The workaround uses a 'dead' argument, just to make a different signature. I'm afraid this argument might cause overhead. The following would be nice, but is not possible in C++.
struct Spaceship { 
    template<class Weapon> virtual void hit()=0;  // not allowed 
}; 

struct XWing : Spaceship { 
    template<class Weapon> void hit() { 
        cout<<"\nX-Wing was hit by a "<<Weapon::toString(); 
    } 
}; 

struct BWing : Spaceship{ 
    template<class Weapon> void hit() { 
        cout<<"\nB-Wing was hit by a "<<Weapon::toString(); 
    } 
} 

//----------------------------------------------------------------------- 

struct Missile { 
    static char* toString() { 
        return "missile"; 
    } 
}; 

struct LaserGun { 
    static char* toString() { 
        return "laser gun"; 
    } 
}; 

//----------------------------------------------------------------------- 

int main() 
{ 
    Spaceship* spaceship = new XWing(); 
    spaceship.hit<Missile>(); 
    char s; 
    cin>>s; 
    return 0; 
}
And this is my workaround.
        Spaceship::AfraidOf<Missile>     Spaceship::AfraidOf<LaserGun> 
                     |_________________________________| 
                                      | 
                               Spaceship::Ship 
             _________________________|________________________ 
            |                |                |                | 
  XWing::AfraidOf<Missile>   |     BWing::AfraidOf<Missile>    | 
            |                |                |                | 
            |    XWing::AfraidOf<LaserGun>    |   BWing::AfraidOf<LaserGun> 
            |________________|                |________________| 
                     |                                   | 
                XWing::Ship                         BWing::Ship
struct Missile { 
    static char* toString() {return "missile";} 
}; 

struct LaserGun { 
    static char* toString() {return "laser gun";} 
}; 

//----------------------------------------------------------------------- 

struct Spaceship { 

    template<class W> struct AfraidOfWeapon { 
        virtual void hit(W* w)=0; 
    }; 
    
    struct Ship : AfraidOfWeapon<Missile>, AfraidOfWeapon<LaserGun> { 
        template<class W> void hit() { 
            W w; 
            ((AfraidOfWeapon<W>*)this)->hit((W*)0); 
        } 
    }; 
}; 

//----------------------------------------------------------------------- 

struct XWing { 
    template<class W> struct AfraidOfWeapon : virtual Spaceship::Ship { 
        void hit(W* w) { 
            cout<<"\nX-Wing was hit by a "<<W::toString(); 
        } 
    }; 
    struct Ship : AfraidOfWeapon<Missile>, AfraidOfWeapon<LaserGun> {}; 
}; 

//----------------------------------------------------------------------- 

int main() 
{ 
    Spaceship::Ship* spaceship = new XWing::Ship(); 
    spaceship->hit<Missile>(); 
    return 0; 
}
Same/similar problem, but without solution: virtual templated member function workaround pure virtual templated methods? Virtual templates possible? Discussion about my workaround (but not enough technical feedback) http://www.c-plusplus.de/forum/viewtopic-var-t-is-165189.html (in german) http://www.velocityreviews.com/forums/t376254-workaround-for-virtual-function-templates.html
deffer
deffer
I think it's too verbose.
You have to manually specify each template parameter that you would like to be using. In my oppinion it's at least as verbose as just creating separate virtual function declaration for each type.

I'm not an expert in multiple inheritance stuff, but I smell O(n2) growth of the size of the class with increase of amount of weapons. But that's just an educated guess.
lhead
lhead
Quote:
Original post by deffer
I think it's too verbose.
You have to manually specify each template parameter that you would like to be using. In my oppinion it's at least as verbose as just creating separate virtual function declaration for each type.

This would make O(n) for the code size. However, I can try to further reduce that number. Imagine the Weapon template argument is not a class, but an integer number. In this case, I can hack a template that inherits from a range of "AfraidOfWeapon" classes. Then we'd be back to O(1) for the written code size.
Now imagine the set of weapons depends on another ('global') template parameter. Then it is impossible for me to separately implement these functions.

Quote:
I'm not an expert in multiple inheritance stuff, but I smell O(n2) growth of the size of the class with increase of amount of weapons. But that's just an educated guess.

The compiled code will have O(n2) that's correct. But your suggestion would make both the compiled code and the written code O(n2)
deffer
deffer
Quote:
Original post by lhead
The compiled code will have O(n2) that's correct. But your suggestion would make both the compiled code and the written code O(n2)


I can make it linear using mixins.
With no performance loss whotsoever. And with easy syntax.

//-----------------------------------------------------------------------struct Missile {   static char* toString() {return "missile";} }; struct LaserGun {   static char* toString() {return "laser gun";} };struct MalpOnAStick {   static char* toString() {return "malp on a stick";} }; struct MachineGun {   static char* toString() {return "machine gun";} }; struct UnimplementedWeapon {   static char* toString() {return "program crasher";} }; //-----------------------------------------------------------------------class Vehicle{public:  virtual ~Vehicle() {};  template<class Weapon>  void hit() { hit_v((Weapon*)0); };private: // linear wrt. amount of weapons  virtual void hit_v(Missile*) = 0;  virtual void hit_v(LaserGun*) = 0;  virtual void hit_v(MalpOnAStick*) = 0;  virtual void hit_v(MachineGun*) = 0;};template<class TBase>class WeaponDelegator  : public TBase{private: // linear wrt. amount of weapons  virtual void hit_v(Missile*)      { TBase::hit_impl<Missile>(); };  virtual void hit_v(LaserGun*)     { TBase::hit_impl<LaserGun>(); };  virtual void hit_v(MalpOnAStick*) { TBase::hit_impl<MalpOnAStick>(); };  virtual void hit_v(MachineGun*)   { TBase::hit_impl<MachineGun>(); };};// weapons are now://   O(n) in v-table size//   O(1) in memory size//   O(1) in execution speed// and most important://   O(1) to implementclass Hoover_base  : public Vehicle{public:  template<class Weapon>  void hit_impl() {   	std::cout<<"Hoover was hit by a "<<Weapon::toString()<<'\n';   };};class XWing_base  : public Vehicle{public:  template<class Weapon>  void hit_impl() {     std::cout<<"X-Wing was hit by a "<<Weapon::toString()<<'\n';   };};class Zeppelin_base  : public Vehicle{public:  template<class Weapon>  void hit_impl() {     std::cout<<"Zeppelin was hit by a "<<Weapon::toString()<<'\n';   };};//----------------------------------------------------------------------- typedef WeaponDelegator<Hoover_base>   Hoover;typedef WeaponDelegator<XWing_base>    XWing;typedef WeaponDelegator<Zeppelin_base> Zeppelin;//----------------------------------------------------------------------- int main(){  Vehicle *tab[3];  tab[0] = new Hoover();  tab[1] = new XWing();  tab[2] = new Zeppelin();  for (int i=0; i<3; ++i)  {    tab->hit<Missile>();    tab->hit<LaserGun>();    tab->hit<MalpOnAStick>();    tab->hit<MachineGun>();    //tab->hit<UnimplementedWeapon>(); // won't compile!  };  return 0;};


Quote:
Original post by lhead
Imagine the Weapon template argument is not a class, but an integer number. In this case, I can hack a template that inherits from a range of "AfraidOfWeapon" classes. Then we'd be back to O(1) for the written code size.
Now imagine the set of weapons depends on another ('global') template parameter. Then it is impossible for me to separately implement these functions.


I think I can modify the Vehicle and WeaponDelegator classes the same way you claim to be possible.
I think some MPL goodies, like list of types, could be helpful here.
lhead
lhead
That looks interesting! I will need to take a closer look on that.
Nitage
Nitage
Wouldn't it be easier to have all weapons inherit from a base class?
t0Xic
t0Xic
No, because you have a vehicle base class and a weapon base class -> double dispatch too.

Maybe a visitor pattern could solve the problem quite smoth...
lhead
lhead
@deffer

Ok, my solution has
- written code size O(#weapons * #vehicles) because of the long inheritance lists. If the weapons are simply numbers, the written code size can be dropped to O(#vehicles) by using some template trickery to generate the inheritance lists.
- compiled code size O(#weapons * #vehicles).

Your solution has
- written code size to O(#weapons + #vehicles) because of explicit overloading for each weapon type. Can be dropped?
- compiled code size to O(#weapons * #vehicles)

The benefit of your solution is that the virtual mechanism with the awkward 'dead parameter' needs to happen only once. It's no longer necessary to think about this when implementing a specific vehicle class.

The compiled and optimized code (everything non-virtual gets inlined if possible) will be absolutely equivalent for both solutions.

The question: Can you drop your written code size to O(#vehicles)? I'd say yes, by using a similar multi-inheritance mechanism as I do. But better, because the awkward mechanics can all be banned into one framework, without the vehicle implementation having to consider that.


                DelegatorBase<Missile>         DelegatorBase<LaserGun>                                     \         /                                       \       /     XWing                           SpaceshipBase                        BWing      | \             ______________/   /   \   \_________________       / |      |  \           /                 /     \                    \     /  |      |   \_________/______           /       \                 ___\___/   |      |            /       \         /         \               /    \      |      |           /         \       /           \             /      \     | DelegatorT<XWing, Missile>  \     /    DelegatorT<BWing, Missile>    \    |         \                    \   /             |                      \   |          \      DelegatorT<XWing, LaserGun>    |   DelegatorT<BWing, LaserGun>            \             /                      |           /             \           /                       |          /           SpaceshipT<XWing>                  SpaceshipT<BWing>



//                                                         general framework//---------------------------------------------------------------------------template<struct Weapon> struct DelegatorBase {    virtual void hit_v(Weapon*)=0;};struct SpaceshipBase :    DelegatorBase<Weapon_1>,  // can be achieved in code size O(1)    ...,    DelegatorBase<Weapon_k>{    template<struct Weapon> hit() {hit_v((Weapon*)0);}};template<struct ShipType, struct Weapon> struct DelegatorT :    virtual ShipType,    virtual SpaceshipBase{    virtual void hit_v(Weapon*) {hit_impl<Weapon>();}};template<struct ShipType> struct SpaceshipT :    DelegatorT<ShipType, Weapon_1>,  // can be achieved in code size O(1)    ...,    DelegatorT<ShipType, Weapon_k>{};//                                                       specific spaceships//---------------------------------------------------------------------------struct Type_XWing {    template<struct Weapon> void hit_impl() {...}};struct Type_StarDestroyer {    template<struct Weapon> void hit_impl() {...}};//                                                          specific weapons//---------------------------------------------------------------------------struct Missile {...};template<int n> struct RailgunT  // Railgun type n{...};//                                                                     usage//---------------------------------------------------------------------------void main() {    SpaceshipBase* spaceship = new SpaceshipT<Type_XWing>();    spaceship->hit<Missile>();}


[Edited by - lhead on November 21, 2006 6:13:14 AM]
deffer
deffer
Quote:
Original post by lhead
Ok, my solution has
- written code size O(#weapons * #vehicles) because of the long inheritance lists. If the weapons are simply numbers, the written code size can be dropped to O(#vehicles) by using some template trickery to generate the inheritance lists.


I really doubt that using some types generated by numbers would be considered a viable solution... [grin]

Quote:
Original post by lhead
- compiled code size O(#weapons * #vehicles).


Yep, that's the minimum and maximum of this particular problem.

Quote:
Original post by lhead
Your solution has
- written code size to O(#weapons + #vehicles) because of explicit overloading for each weapon type. Can be dropped?


It depends on what you consider being O(#weapons).
In my oppinion the following line is O(#weapons), but it's by no means a drawback, and IMO cannot be avoided.

typedef boost::mpl::list weapon_types;

Full, refined source(1):
#include <iostream>#include <boost/mpl/list.hpp>#include <boost/mpl/pop_front.hpp>#include <boost/mpl/empty.hpp>#include <boost/mpl/front.hpp>#include <boost/mpl/size.hpp>//-----------------------------------------------------------------------// details, no need to bother looking.//-----------------------------------------------------------------------namespace detail{  using namespace boost;  // base class  template<typename TList, int TSize>  class VehicleBaseImpl;  template<typename TList, int TSize>  class VehicleBaseImpl    : public VehicleBaseImpl< typename mpl::pop_front<TList>::type,                              TSize-1 >  {    typedef VehicleBaseImpl< typename mpl::pop_front<TList>::type, TSize-1 >  base_type;    typedef typename mpl::front<TList>::type  weapon_type;  protected:    virtual void hit_v(weapon_type*) = 0;    using base_type::hit_v;  };  template<typename TListEmpty>  class VehicleBaseImpl< TListEmpty, 0 >  {  public:    virtual ~VehicleBaseImpl< TListEmpty, 0 >() {};  private:    void hit_v(void*) {};  };  // wrapper class  template< class Base, typename TList, bool Empty >  class VehicleWrapperImpl;  template<class Base, typename TListEmpty>  class VehicleWrapperImpl< Base, TListEmpty, true >    : public Base  {  };  template<class Base, typename TList>  class VehicleWrapperImpl< Base, TList, false >    : public VehicleWrapperImpl< Base,                    typename mpl::pop_front<TList>::type,                    mpl::empty<typename mpl::pop_front<TList>::type>::value >  {    typedef typename mpl::front<TList>::type  weapon_type;    virtual void hit_v(weapon_type*) {      // g++ doesn't eat explicit template function instantiation      // inside class template.      hit_impl((weapon_type*)0);    };  };};//-----------------------------------------------------------------------// more details (still don't bother looking)//-----------------------------------------------------------------------template<typename TList>class VehicleBaseT  : public detail::VehicleBaseImpl< TList, boost::mpl::size<TList>::value >{public:  typedef TList _weapon_list_type;public:  template<class Weapon>  void hit() {    hit_v((Weapon*)0);  };};template<class Base>class VehicleWrapper  : public detail::VehicleWrapperImpl< Base,                    typename Base::_weapon_list_type,                    boost::mpl::empty<typename Base::_weapon_list_type>::value >{};//-----------------------------------------------------------------------// user defined code : weapons!//-----------------------------------------------------------------------struct Missile {     static char* toString() {return "missile";} }; struct LaserGun {     static char* toString() {return "laser gun";} };struct MalpOnAStick {     static char* toString() {return "malp on a stick";} }; struct MachineGun {     static char* toString() {return "machine gun";} }; struct UnimplementedWeapon {     static char* toString() {return "program crasher";} }; //-----------------------------------------------------------------------// what user has to additionally maintain on his own.//-----------------------------------------------------------------------typedef boost::mpl::list<Missile, LaserGun, MalpOnAStick, MachineGun> weapon_types;typedef VehicleBaseT<weapon_types> VehicleBase;//-----------------------------------------------------------------------// user defined code : vehicles!//-----------------------------------------------------------------------class Hoover_base  : public VehicleBase{public:  template<class Weapon>  void hit_impl(Weapon*) {     std::cout<<"Hoover was hit by a "<<Weapon::toString()<<'\n';   };};class XWing_base  : public VehicleBase{public:  template<class Weapon>  void hit_impl(Weapon*) {     std::cout<<"X-Wing was hit by a "<<Weapon::toString()<<'\n';   };};class Zeppelin_base  : public VehicleBase{public:  template<class Weapon>  void hit_impl(Weapon*) {     std::cout<<"Zeppelin was hit by a "<<Weapon::toString()<<'\n';   };};typedef VehicleWrapper<Hoover_base>   Hoover;typedef VehicleWrapper<XWing_base>    XWing;typedef VehicleWrapper<Zeppelin_base> Zeppelin;int main(){  VehicleBase *tab[3];  tab[0] = new Hoover();  tab[1] = new XWing();  tab[2] = new Zeppelin();  for (int i=0; i<3; ++i)  {    tab->hit<Missile>();    tab->hit<LaserGun>();    tab->hit<MalpOnAStick>();    tab->hit<MachineGun>();    //tab->hit<UnimplementedWeapon>(); // won't compile!  };  return 0;};


Quote:
Original post by lhead
The compiled and optimized code (everything non-virtual gets inlined if possible) will be absolutely equivalent for both solutions.


I don't think so - each instance of your class is O(#weapons) (because of the multiple inheritance (from classes with v-table), your class needs to keep pointers to v-table of each of its subclass). Each instance of my class is O(1).


Quote:
Original post by lhead
The question: Can you drop your written code size to O(#vehicles)?


This is impossible, since you have to provide definition for vehicles AND weapons. Of course, once all the weapons' definitions are provided, each vehicle definition is O(1).




(1)
g++ compiles and runs the code just fine.
vs2005ee gives ICE, unfortunately... [grin]
But my whole argumentation stands for the code I provided in my earlier post, just as well.
lhead
lhead
I need to study that boost solution! Until then,

Original post by deffer
Quote:
I really doubt that using some types generated by numbers would be considered a viable solution... [grin]

Depends on what you are planning. The spacefight scenario is just an example, I want to use this for something else.. (don't want to explain). There the main structure gets a template parameter that defines which 'weapon types' can be used. (in my case weapon = command in a state machine, more or less)

Quote:
Quote:
Original post by lhead
Your solution has
- written code size to O(#weapons + #vehicles) because of explicit overloading for each weapon type. Can be dropped?

It depends on what you consider being O(#weapons).
In my oppinion the following line is O(#weapons), but it's by no means a drawback, and IMO cannot be avoided.

typedef boost::mpl::list weapon_types;

Full, refined source(1):
*** Source Snippet Removed ***


Ideally, I want the weapons to be defined automatically. If this is not possible, I want to avoid redundancy, so I only need to define that once. The boost mechanism may do the job, I need to have a closer look!

Quote:
Quote:
Original post by lhead
The compiled and optimized code (everything non-virtual gets inlined if possible) will be absolutely equivalent for both solutions.


I don't think so - each instance of your class is O(#weapons) (because of the multiple inheritance (from classes with v-table), your class needs to keep pointers to v-table of each of its subclass). Each instance of my class is O(1).

Sorry I really don't know so much about the vtable. What I know is, if an object supports virtual functions, it must store a pointer to its class. This way, c++ can find out which implementation of the function it has to use. But the vtable is something different, right?

deffer
deffer
Quote:
Original post by lhead
Sorry I really don't know so much about the vtable. What I know is, if an object supports virtual functions, it must store a pointer to its class.


This is not how the virtual function are implemented.
You can find more about it here: http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.4. Multiple inheritance gets much, much more complicated.
lhead
lhead
Quote:
Original post by defferThis is not how the virtual function are implemented.
You can find more about it here: http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.4. Multiple inheritance gets much, much more complicated.

Ok, I will continue reading that.

One problem seems to be that all those helper classes get their own vtable. Isn't there a way the compiler can get rid of those helper classes via optimization? Or maybe I can declare those classes abstract, then there is no reason at all to define a vtable.

(this is my pov before reading the complete article)
lhead
lhead
I took a closer look at your boost solution.

The difference I found is you are using serial mixins instead of parallel mixins. However, you still have as many helper classes as I have. Do serial mixins behave better regarding the vtable?
Darragh
Darragh
Quote:

struct Spaceship {
template virtual void hit()=0; // not allowed
};


I'm no expert yet on templates, and you are probably already aware of this, but the reason I suspect this is not allowed is because template functions are expanded into real functions (aka actual code) at compile time, and since a virtual function is never expanded (its just a pointer to a function under the hood) you have a problem therein.

I don't know about this design though. It seems needlessly over complex for what you are trying to do. Templates are something which should only be used sparingly and with very good reason. They're excellent for use with ADTs (Abstract data types) such as linked lists and such but I cant see why you would want to use them in this instance here when an inheritance hierarchy combined with polymorphisim would do.

Why not create generalised classes for Vehicles and Weapons with abstracted, virtual functions and just inherit from them? If you wanted to take things further you could also create a 'GameObject' class from which all objects in the game are derived. The GameObject class may or may not be needed though depending on what exactly you are doing. As is always the case with inheritance, you should only use it when there is a need to factor out common and redundant functionality/attributes across your classes.

Also why are you using 'structs' instead of classes ? Structs are a holdover from C (they're still useful for just defining collections of raw data however) but I don't understand why you would want to use them instead of classes. Structs are typically used for just data, whereas classes are used to group data AND functionality.

Quote:
I would like to get some technical feedback ! Especially about the performance.


You should only worry about performance once you have all your code working. Slow and working code is far better than fast code that doesn't work at all! How many objects do you intend on having in your game anyhow ? If you're only using a limited number of objects then the performance difference will be miniscule. On the other hand if you have hundreds of thousands then it WILL make an impact. Regardless, you'll probably find a much greater performance boost by optimising drawing, collision detection and spatial partitioning code than doing micro-optimsations of your classes/structs. Just some food for thought..

Anyhow best of luck with your game.

Regards,
Darragh.
deffer
deffer
Quote:
Original post by lhead
The difference I found is you are using serial mixins instead of parallel mixins. However, you still have as many helper classes as I have. Do serial mixins behave better regarding the vtable?

EXAMPLE:
struct A{  virtual void a1();  virtual void a2();  // A's member variables};

How does A look in memory? Most likely (in pseudo-code):
static v_table_A[2] = {  &A::a1,  &A::a2};struct A{  &v_table_A  // A's member variables};

Now, let's add simple inheritance:
struct B : public A{  virtual void b1();  virtual void b2();};// memory layout (pseudo-code):static v_table_B[4] = {  &A::a1,  &A::a2,  &B::b1,  &B::b2};struct B{  &v_table_B,  // A's member variables  // B's member variables};

And so on, for entire inheritance chain. In every case, the v-table pointer is the first member. Now, if we'd want to do...
B *b = new B();A *ab = b;

... we can do it safely because, in memory, B is inluding A and B's v-table is including A's v-table. It's safe to do trivial casting for entire inheritance chain.



Imagine the same trick for simple multiple inheritance:
struct A{  virtual void a1();  virtual void a2();};struct B{  virtual void b1();  virtual void b2();};struct C  : public A, public B{};// now do this safely:C *c = new C();A *a = c;B *b = c;a->a1();b->b1();


The code for calling a->a1() and b->b1() must be the same as if it were for real classes A and B, respectively
AND
A's v-table consists only of A::a1 and A::a2 functions
AND
B's v-table consists only of B::b1 and B::b2 functions
AND
C is to have only one v-table pointer
=>
C's v-table would have to begin with A's methods
AND
C's v-table would have to begin with B's methods
=>
contradiction
=>
C is not having single v-table, it's having (at least) two v-tables.




Real (most likely) memory layout:
// pseudo-code:static v_table_A[2] = {  &A::a1,  &A::a2,};static v_table_B[2] = {  &B::b1,  &B::b2};struct C{  &v_table_A,  // A's member variables  &v_table_B,  // B's member variables};


With this implementation, casting from C* to A* gives an object having same memory address as the original, but casting from C* to B* gives an address starting in the middle of the original object.
Vorpy
Vorpy
I am wondering why you are using templates at all. Wouldn't passing an object that describes the weapon as an argument to the function work just as well and be easier to understand? As far as considering the performance, trying to squeeze in templates seems like premature and probably unnecessary optimization.

Templates can be difficult to use, and therefore even in systems where you are planning to use templates it is recommended to write a non-templated version first. The non-templated version will be simpler and easier to debug. For what you're doing, I'm not sure if templates make sense at all.
deffer
deffer
Quote:
Original post by Darragh
...

Quote:
Original post by Vorpy
...


Hard not to agree, that template usage in this case makes things over-complicated, and that ideally arguments should be passed by POD structs here.

But - some people like to program just for the pure fun of it. Don't spoil all the fun. [smile]
lhead
lhead
For the fun, yes :)
The example I gave here is nice for analysing the mechanisms. It is bad for showing why I want templates. My project at home looks quite different, but I don't see the point in posting an over-complicated code.

The main purpose of my question was to analyse what is possible with templates, and what are the consequences for performance.

So it really doesn't help much to talk about non-template alternatives.

By the way, what is a POD struct ??

[EDIT:] hehe I found it :)

@ deffer
I made an experiment with different types of inheritance and the sizeof operator, and it seems you are right! Better not use runtime polymorphism with multi-inheritance.

[Edited by - lhead on November 22, 2006 8:17:53 AM]
lhead
lhead

I'm a little confused..
The 'serial mixins' (as I call it) overwrite each others' function declarations, even though they have a different signature! This sucks..

#include<iostream>using namespace std;struct A_Base {static void foo() {cout << "A0::foo()\n";}};struct A0 : A_Base {static void foo() {cout << "A0::foo()\n";}};struct A1 : A_Base {static void foo() {cout << "A1::foo()\n";}};struct A2 : A_Base {static void foo() {cout << "A2::foo()\n";}};struct Serial_A0             {virtual void foo(A0*) {A0::foo();}};struct Serial_A1 : Serial_A0 {virtual void foo(A1*) {A1::foo();}};struct Serial_A2 : Serial_A1 {virtual void foo(A2*) {A2::foo();}};int main(){    Serial_A2().foo((A1*)0);    return 0;}


test.cpp: In function `int main()':test.cpp:429: error: no matching function for call to `Serial_A2::foo(A1*)'test.cpp:424: note: candidates are: virtual void Serial_A2::foo(A2*)


The same class hierarchy is generated by the boost solution, and you said it compiles well? What's wrong then with my little example?
lhead
lhead

I'm a little confused..
The 'serial mixins' (as I call it) overwrite each others' function declarations, even though they have a different signature! This sucks..

#include<iostream>using namespace std;struct A_Base {static void foo() {cout << "A0::foo()\n";}};struct A0 : A_Base {static void foo() {cout << "A0::foo()\n";}};struct A1 : A_Base {static void foo() {cout << "A1::foo()\n";}};struct A2 : A_Base {static void foo() {cout << "A2::foo()\n";}};struct Serial_A0             {virtual void foo(A0*) {A0::foo();}};struct Serial_A1 : Serial_A0 {virtual void foo(A1*) {A1::foo();}};struct Serial_A2 : Serial_A1 {virtual void foo(A2*) {A2::foo();}};int main(){    Serial_A2().foo((A1*)0);    return 0;}


test.cpp: In function `int main()':test.cpp:429: error: no matching function for call to `Serial_A2::foo(A1*)'test.cpp:424: note: candidates are: virtual void Serial_A2::foo(A2*)


The same class hierarchy is generated by the boost solution, and you said it compiles well? What's wrong then with my little example?

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.