In addition to JoshD's suggestion, you can keep the door open for other transformations by using the visitor pattern.
Add a method dispatch_visit
to the Sprite hierarchy, using covariant return types:
class Sprite{
virtual HTMLSprite * dispatch_visit( HTMLConvert_ const &c ) const = 0;
};
class Character : public Sprite {
virtual HTMLCharacter * dispatch_visit( HTMLConvert_ const &c ) const
{ return c( this ); }
};
class Markup : public Sprite {
virtual HTMLMarkup * dispatch_visit( HTMLConvert_ const &c ) const
{ return c( this ); }
};
This allows each object to notify the converter of its dynamic type — essentially, a dynamic dispatch into the parallel type, or even a parallel type hierarchy. Everything else pretty much works as your code is written… the best candidate is chosen among the operator()()
functions of the converter, based on the static parameter type.
Oh, you'll need to add the "missing function" to the converter:
struct HTMLConvert_ {
HTMLSprite * operator () (const Sprite * c) { return c->dispatch_visit( *this ); }
HTMLCharacter * operator () (const Character * c) { return new HTMLCharacter (); }
HTMLMarkup * operator () (const Markup * c) { return new HTMLMarkup (); }
} HTMLConvert;
Hmm, that repetitive function could be encapsulated by a template… this only seems to work in C++0x if you want the visitable
template to automatically determine the return type of dispatch_visit
. If you don't like principal_base
you can factor it out.
#include <functional>
template< class T >
struct principal_base
{ typedef void type; };
template< class Client, class Visitor,
class Base = typename principal_base< Client >::type >
struct visitable :
virtual visitable< typename principal_base< Client >::type, Visitor > {
virtual typename std::result_of< Visitor( Client * ) >::type
dispatch_visit( Visitor const &v ) const
{ return v( static_cast< Client const * >( this ) ); }
};
template< class Client, class Visitor >
struct visitable< Client, Visitor, void > {
virtual typename std::result_of< Visitor( Client * ) >::type
dispatch_visit( Visitor const &v ) const = 0;
};
class HTMLSprite { /* ... */ };
class HTMLCharacter : public HTMLSprite {};
class HTMLMarkup : public HTMLSprite {};
class Sprite;
class Character;
class Markup;
struct HTMLConvert_ {
HTMLSprite * operator () (const Sprite * c);
HTMLCharacter * operator () (const Character * c);
HTMLMarkup * operator () (const Markup * c);
} HTMLConvert;
class Sprite : public visitable< Sprite, HTMLConvert_ > {};
template<> struct principal_base< Character >
{ typedef Sprite type; };
class Character : public Sprite, visitable< Character, HTMLConvert_ > {};
template<> struct principal_base< Markup >
{ typedef Sprite type; };
class Markup : public Sprite, visitable< Markup, HTMLConvert_ > {};
//class Invalid : Character, Markup {};
HTMLSprite * HTMLConvert_::operator () (const Sprite * c)
{ return c->dispatch_visit( *this ); }
HTMLCharacter * HTMLConvert_::operator () (const Character * c)
{ return new HTMLCharacter (); }
HTMLMarkup * HTMLConvert_::operator () (const Markup * c)
{ return new HTMLMarkup (); }