Template Specialization
-
Tag zusammen,
ich habe eine Template-Klasse "ResourceManager<T>", mit deren Hilfe ich bestimmte Medien (Bilder, Sounds) aus einer Multimedia-Library laden kann. Dabei ist es erforderlich, dass T eine Methode Load( const std::string& ) beinhaltet.
Nun ist es aber so, dass es auch einige Typen gibt, die mehr als nur einen std::string-Parameter erfordern. Hier dachte ich dann an Template Specialization.
Z.B. werden Fonts nicht nur mit einem Dateinamen, sondern ebenfalls mit einer Schriftgröße geladen (Load( const std::string&, unsigned int)). Liege ich richtig in der Annahme, dass ich die spezialisierte Version fast vollständig neu schreiben muss?
Der Vorteil ist ja, dass ich weiterhin ResourceManager<Font> benutzen könnte, aber den Code so heftig zu vervielfältigen ist nicht gerade das, was ich mir dadurch erhoffte. Wenn das der Fall ist, würde ich wohl eher von ResourceManager<T> ableiten und die erweiterte Load()-Methode neu implementieren.
Bisher dachte ich, dass sich das Spezialisieren ähnlich verhält wie ein Vererben, nur halt zur Kompilierzeit. Wohl daneben gelegen.

Was meint ihr?
-
MarkBol schrieb:
Wohl daneben gelegen.

Was meint ihr?Lagere das, was du spezialisieren musst in eine kleine templateisierte Basisklasse oder einen Member aus und spezialisiere nur den.
-
Klingt gut!
Ich habe nun die Basisfunktionalität in eine Basisklasse verschoben, welche ich für spezialisierte Versionen heranziehe.
Danke.

-
Das sind meiner Meinung nach zwei Paar Schuhe weil die Funktionssignaturen unterschiedlich sind. Insofern weiss ich nicht, ob der Ansatz besonders glücklich gewählt ist. Zur Not kannst du Function Objects benutzen, deren Parameter du über den Konstruktor zur Verfügung stellst:
#include <string> struct SimpleLoader { std::string Filename_ SimpleLoader( const std::string& FileName ) : FileName_( FileName ) { } void operator()() { // Mach was } }; struct AdvancedLoader { int Param_; std::string FileName_; AdvancedLoader( const std::string& FileName, int Param ) : FileName_( FileName ), Param_( Param ) { } void operator()() { // mach was anderes, diesmal mit Param_ } }; template<typename LoaderType> class ResourceLoader { LoaderType Loader_; public: ResourceLoader( const LoaderType& Loader ) : Loader_( Loader ) { } void load_resource() { // benutzer operator() des Loaders zum Laden der Resource Loader_(); } }; int main() { ResourceLoader<SimpleLoader> r1( SimpleLoader( "c:/data.dat" ) ); r1.load_resource(); ResourceLoader<AdvancedLoader> r2( AdvancedLoader( "c:/data.dat", 1 ) ); r2.load_resource(); }Um das weiter zu verfeinern kannst du die Loader Klasse den Rückgabetyp der load_resource() Methode bestimmen lassen:
struct BitmapLoader { typedef Bitmap* return_type; return_type operator()() { return new Bitmap(); } }; struct SoundLoader { typedef Sound* return_type; return_type operator()() { return new Sound(); } }; template<typename LoaderType> class ResourceLoader { typedef typename LoaderType::return_type return_type; LoadType Loader_; public: ResourceLoader( const LoaderType& Loader ) : Loader_( Loader ) { } return_type load_resource() { return Loader_(); } };Wenn du das schön machen willst kannst du statt raw pointer smart_ptr zurückgeben, und wenn du es besonders schön machen willst kannst über allocator policies steuern, ob du raw oder smart Pointer zurückgeben willst.