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?
There is a template hack to do it at compile time.
First you need to create an interface class like this:
The idea is: Cast
this
toT
and call method with the same name and the same arguments from it. As you can see the wrapper function is inline so there will be no performance or call stack overheads during runtime.And then create classes implementing the interface like this:
Then just add implementations of the derived methods normally.
This way might not look beautiful but exactly does the job.
First, your code is doing sizeof of a pointer instead of a dereferenced pointer so it wouldn't work even if gcc wasn't complaining.
Second, sizeof trick have to work with casts of 0 and not actual pointers or object - that guarantees zero overhead and also that it won't complie till you do it right.
3rd, you need to declare 2 templated classes or structs one deriving just from D, the other deriving from D and virtual B, and then cast 0 to their pointers, dereference them and then sizeof.
4th - Do you have any big reason for trying to be politically correct with static_cast instead of straight cast here? Compiler will always infer from that that you are looking for more whining and in this case you are definitelly not.
BTW you don't need to grab full code from Alexandrescu - just grab the core technique which basically just:
Alexandrescu is really good at cleaning up after a trick.
Oh and remember that compiler is not supposed to evaluate sizeof args or instantiate unused templated classes and structs - so if it does then it's a compiler bug and if you force it to do that then it's your bug :-)
Once you have that, you need to define precisely and in positive terms wha your statement "if a pointer to Derived can be cast from a pointer to Base without dynamic_cast<>" actually means in terms of class relationships - just saying "without operator/function Q" doesn't makea problem well defined and you can't solve what you can't define - honest :-)
So just take the first clean step that compiles and then try to define in which way would the two cases you mentioned be different in reality - what would one have or do that the other one wouldn't.