So, one problem pattern that I keep coming across and don't have a good solution for is how to provide template specializations that are based in what type a template parameter is derived from. For example, suppose I have:
template<typename T>
struct implementPersist;
template<typename T>
void persist( T& object )
{
implementPersist::doPersist( object );
}
What I'd like is for users of persist to be able to provide implementations of implementPersist::persist for types that are declared after the above. That's straightforward in principle, but cumbersome in practice but the user need to provide an implementPersist for every type.
To be more clear, suppose I have:
struct Persistent { virtual void myPersist() = 0; };
struct MyClass : public persistent { virtual void MyPersist() { ...implementation...} };
// Persists subclasses of Persistent using myPersist
template<>
struct implementPersist<Persistent>{ void doPersist(Persistent& p) { p->myPersist(); } };
struct X{};
template<>
struct implementPersist<X>{ void doPersist(X& p) { ...implementation...} };
// Persists subclasses of Persistent using boostPersist
struct MyBoostPersistedObject { virtual void boostPersist() = 0 };
struct Z : public MyBoostPersistedObject { virtual void boostPersist() = 0 };
template<>
struct implementPersist<myBoostPersistedObject>{ void boostPersist() { ...implementation... } };
My intention is that I provide one template implementation for all subclasses of Persist, another for all subclasses of myBoostPersistedObject and other for miscellaneous classes not in interesting classes structures (e.g. various POD types). In practice however,
implementPersist<Persistent>::doPersist
is only ever invoked if ::persist(T&) is called where T is exactly a Persistent object. It falls back to the (missing) generic case where T=myClass. In general, I want to be able to specialize templates in generic ways based on inheritance. Its little frustrating because clearly compilers know how to do this, and do it when deciding to call functions based upon parameters, e.g.
void persist( Persistent&); void persist( X& ); void persist( myBoostPersistedObject& );
But as far as I can tell, no such similar matching can be done for templates.
One workaround is to do something like:
class persist;
template<typename T, bool hasMyPersistMethod=isDerivedFrom(T,persist)::value >
struct implementPersist;
template<typename T, bool true >
struct implementPersist<T,true>
{
template<> struct implementPersist<X>{ void doPersist(T& p) { p->myPersist(); } }
};
(see here for isDerivedFrom).
However, this requires that the initial declaration of implementPersist knows about the types of the classes providing implementations. I'd like something more generic.
I'm frequently finding uses for such a pattern, in order to avoid adding explicit specializations for every class in my system.
Any ideas?