Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: Can virtual inheritance be detected at compile time?

Tags:

I would like to determine at compile time if a pointer to Derived can be cast from a pointer to Base without dynamic_cast<>. Is this possible using templates and metaprogramming? This isn't exactly the same problem as determining if Base is a virtual base class of Derived, because Base could be the super class of a virtual base class of Derived.

Thanks, Tim Update: I felt good about this method:

#include <iostream>  using namespace std;  class Foo { };  class Bar : public Foo { };  class Baz : public virtual Foo { };  class Autre : public virtual Bar { };  typedef char Small; class Big { char dummy[2]; };  template<typename B, typename D> struct is_static_castable {     const B* foo;     char bar[1];     static Small test(char(*)[sizeof(static_cast<const D*>(foo)) == sizeof(const D*)]);     static Big test(...);     enum { value = (sizeof(test(&bar)) == sizeof(Small)) }; };  int main() {      cout << "Foo -> Bar: " << is_static_castable<Foo, Bar>::value << "\n";     cout << "Foo -> Baz: " << is_static_castable<Foo, Baz>::value << "\n";     cout << "Foo -> Autre: " << is_static_castable<Foo, Autre>::value << "\n"; } 

But it doesn't work with gcc:

multi-fun.cpp: In instantiation of ‘is_static_castable<Foo, Baz>’: multi-fun.cpp:38:   instantiated from here multi-fun.cpp:29: error: cannot convert from base ‘Foo’ to derived type ‘Baz’ via virtual base ‘Foo’ multi-fun.cpp:29: error: array bound is not an integer constant multi-fun.cpp: In instantiation of ‘is_static_castable<Foo, Autre>’: multi-fun.cpp:39:   instantiated from here multi-fun.cpp:29: error: cannot convert from base ‘Foo’ to derived type ‘Autre’ via virtual base ‘Bar’ multi-fun.cpp:29: error: array bound is not an integer constant 

Am I confused about what can be done with the sizeof() trick?

like image 809
Tim Avatar asked May 23 '10 22:05

Tim


2 Answers

I had the same problem, once. Unfortunately, I'm not quite sure about the virtual-problem. But: Boost has a class named is_base_of (see here) which would enable you to do smth. like the following

BOOST_STATIC_ASSERT((boost::is_base_of<Foo, Bar>::value)); 

Furthermore, there's a class is_virtual_base_of in Boost's type_traits, maybe that's what you're looking for.

like image 127
mefiX Avatar answered Oct 09 '22 03:10

mefiX


Here is a solution for redirecting the compiler to do something depending on whether the class is a subclass of another or not.

class A  {};  class B : virtual public A {};  class C : public A {};  // Default template which will resolve for  // all classes template  < typename T , typename Enable = void  > struct FooTraits {     static void foo(){         std::cout << "normal" << std::endl;     } };  // Specialized template which will resolve // for all sub classes of A template  < typename T  > struct FooTraits      < T     , typename boost::enable_if          < boost::is_virtual_base_of< A, T>          >::type     > {     static void foo(){         std::cout << "virtual base of A" << std::endl;     } };  int main(int argc, const char * argv[] ){     FooTraits<C>::foo(); // prints "normal"     FooTraits<B>::foo(); // prints "virtual base of A" } 

and if you want to know how boost did it. If you have class Base and class Derived then the following holds.

struct X : Derived, virtual Base  {    X();    X(const X&);    X& operator=(const X&);    ~X()throw(); };  struct Y : Derived  {    Y();    Y(const Y&);    Y& operator=(const Y&);    ~Y()throw(); };  bool is_virtual_base_of = (sizeof(X)==sizeof(Y))); 

It's a trick of using virtual inheritence with multiple inheritience. Multiple inheritience from the same virtual base does not result in duplicates of the virtual base class and therefore you can test this with sizeof.

like image 41
bradgonesurfing Avatar answered Oct 09 '22 02:10

bradgonesurfing