Wednesday, April 09, 2008

Stupid C++ tricks

Ever wanted to downcast something, but not too far down? Me either really, but just in case I came up with this:

template<typename Target, typename Excluding, typename Source>
struct exclusive_cast {
static Target* cast(Source *s) {
if( ! dynamic_cast<Excluding *>(s) ) {
return dynamic_cast<Target *>(s);
}
return NULL;
}
};

template<typename Ex1, typename Ex2>
struct ExcludingBoth {};

template<typename Target, typename Ex1, typename Ex2, typename Source>
struct exclusive_cast<Target, ExcludingBoth<Ex1, Ex2>, Source> {
static Target* cast(Source *s) {
if( ! dynamic_cast<Ex1 *>( s ) ) {
return exclusive_cast<Target, Ex2, Source>::cast(s);
}
return NULL;
}
};

That was the header, exclusive_cast.hpp. Now here's a little test program that makes sure it does what I think it does:

#include <iostream>
#include "exclusive_cast.hpp"

class A {
virtual void rtti_rules() {}
};
class B : public A {};
class C : public B {};
class D : public B {};
class E : public B {};
int main() {
B b;
C c;
D d;
E e;
A *ab = &b;
A *ac = &c;
A *ad = &d;
A *ae = &e;

if( exclusive_cast<B, C, A>::cast(ab) ) {
std::cout << "correct" << std::endl;
} else {
std::cout << "incorrect" << std::endl;
}

if( exclusive_cast<B, C, A>::cast(ac) ) {
std::cout << "incorrect" << std::endl;
} else {
std::cout << "correct" << std::endl;
}

if( exclusive_cast<B, ExcludingBoth<C, D>, A>::cast(ad) ) {
std::cout << "incorrect" << std::endl;
} else {
std::cout << "correct" << std::endl;
}
typedef exclusive_cast<B, ExcludingBoth<C, ExcludingBoth<D, E> >, A> complicated;

if( complicated::cast(ae) ) {
std::cout << "incorrect" << std::endl;
} else {
std::cout << "correct" << std::endl;
}
if( complicated::cast(ad) ) {
std::cout << "incorrect" << std::endl;
} else {
std::cout << "correct" << std::endl;
}
if( complicated::cast(ac) ) {
std::cout << "incorrect" << std::endl;
} else {
std::cout << "correct" << std::endl;
}
if( complicated::cast(ab) ) {
std::cout << "correct" << std::endl;
} else {
std::cout << "incorrect" << std::endl;
}
return 0;
}


A possible improvement might be to nest the template parameter Source inside the struct for the cast function so wouldn't need to name Source necessarily.

No comments: