Regestrierung von Template Klassen während Compile-Time



  • Hallo,

    ich möchte gerne Template Klassen während Compile-Time registrieren um sie später via Namen instanzieren zu können.
    Ich hab mir dazu bereits einige Lösungen im Internet angeschaut, leider aber keine passende gefunden.
    Die vielverspechenste Lösung, die ich fand, ist diese:

    struct MyClasses {
        static vector<string> myclasses;
        MyClasses(string name) { myclasses.push_back(name); }
    };
    
    #define REGISTER_CLASS(cls) static MyClasses myclass_##cls(#cls);
    
    struct XYZ {
    };
    
    REGISTER_CLASS(XYZ);
    

    Allerdings schreibt der Autor der Lösung:

    I don't think it will work for templates, though.

    Mein SourceCode nutzt aber Templates. Ich hab nun folgendes probiert, aber es klappt nicht 😞

    template<typename TF>
            class AbstractFilter : public IFilter<TF>
            {
            public:
    
                template<typename T> static IFilter<TF>* createTemplate() {return new T;}
    
                struct FilterFactory
                {
                    typedef boost::unordered_map<std::string, IFilter<TF>*(*)()> FilterMap;
    
                protected:
                    static FilterMap* getMap()
                    {
                        static FilterMap filters;
                        return &filters;
                    }
                };
    
                virtual bool process(TF *data) = 0;
            };
    
    #define REGISTER_CLASS(cls) template<typename TF> static typename AbstractFilter<TF>::FilterFactory filters_##cls(#cls);
    

    Könnt ihr mir sagen, was ich ändern muss, um die obrige Lösung für Templates anzupassen?

    LG
    Daniel


  • Mod

    Könnte im Prinzip so aussehen:

    #include <vector>
    #include <typeinfo>
    #include <string>
    
    using namespace std;;
    
    struct MyClasses {
        static vector<string> myclasses;
        MyClasses(string name) { myclasses.push_back(name); }
    };
    vector<string> MyClasses::myclasses;
    
    template <typename T>
    struct foo
    {
        static const MyClasses reg;
        char dummy[&reg!=0];
    };
    template <typename T>
    const MyClasses foo<T>::reg(string("foo<")+typeid(T).name()+string(">"));
    
    #include <iostream>
    #include <iterator>
    #include <algorithm>
    int main()
    {
        (void)sizeof(foo<int>); // implizite Instantiierung von foo<int> - ein explizite Instantiierung würde nichts demonstrieren, weil eine solche immer automatisch alle Member instantiiert
        copy(MyClasses::myclasses.begin(),MyClasses::myclasses.end(),ostream_iterator<string>(cout,"\n"));
    }
    

    Statische Member werden nur instanziiert, wenn sie auch existieren müssen. dummy koppelt hier die implizite Instanziierung des statischen Members reg an die Instanziierung der Klasse foo<T>, in der Praxis wäre es wahrscheinlich besser, an die Instantiierung einer Memberfunktion (Konstruktor,Destruktor) zu koppeln - jedenfalls dann, wenn diese sowieso selbst geschrieben werden müssen.



  • Hallo,

    zunächst mal Danke für deine Antwort.
    Aber ich verstehe die Lösung nicht 100%-ig und auch nicht wie ich sie auf meinenen Code umsezten kann.
    Ich versuche mein derzeitigen Code nochmal etwas detailierter zu erklären und vielleicht könntest du dann explizit darauf eingehn.

    Zunächst erkläre ich die Klasse AbstractFilter etwas besser:

    //AbstractFilter.hpp
    
    // Die AbstractFilter Klasse ist zuständig für die Bereitstellung der Factory sowie für das entgegenehmen von Regestrierungen.
    // Sie ist selbst eine Template Klasse
    template<typename TF>
            class AbstractFilter : public IFilter<TF>
            {
            public:
                template<typename T> static IFilter<TF>* createTemplate() {return new T;}
                struct FilterFactory
                {
                    // Diese Map legt das mapping zwischen Namen und Konstruktoren fest.
                    typedef boost::unordered_map<std::string, IFilter<TF>*(*)()> FilterMap;
    
                    // Die Factory kann durch einen Table-Lookup die konkrete Klasse instanzieren.
                    static IFilter<TF>* createFilterInstance(const std::string &name)
                    {
                        typename FilterMap::iterator it = getMap()->find(name);
                        if(it != getMap()->end())
                            return it->second();
                        else
                            return NULL;
                    }
    
                protected:
                    //Statische Methode zur erzeugung der FilterMap
                    static FilterMap* getMap()
                    {
                        static FilterMap filters;
                        return &filters;
                    }
                };
    
                // Der FilterRegister ist für die entgegenahme von Registrierungen zuständig.
                template<typename T>
                struct FilterRegister : FilterFactory
                {
                    FilterRegister(const std::string& name)
                    {
                        fprintf(stderr, "Registered Filter: %s\n", name.c_str());
                        FilterFactory::getMap()->insert(typename FilterFactory::FilterMap::value_type(name, &createTemplate<Td>));
                    }
                };
    
                // Pure virtual Function. Verarbeitet Daten beliebigen Typs.
                virtual bool process(TF *data) = 0;
            };
    

    So, die konkrete Implementierung eines Filters schaut zb. folgend aus:

    //SampleFilter.h
    
    class SampleFilter : public AbstractFilter<std::string>
            {
                public:
                    SampleFilter();
                    bool process(std::string *data);
    
                private:
                    static FilterRegister<SampleFilter> reg;
            };
    
    //SampleFilter.cc
    
    #include "samplefilter.h"
    
    using namespace ccns::processing;
    
    //Diese Initialisierung sollte die Klasse SampleFilter registrieren.
    typename ccns::processing::AbstractFilter<std::string>::FilterRegister<SampleFilter> SampleFilter::reg(std::string("SampleFilter"));
    
    SampleFilter::SampleFilter()
    {
    }
    
    bool SampleFilter::process(std::string *data)
    {
        fprintf(stderr, "Processcing Data = %s...\n",data->c_str());
        return true;
    }
    

    Zur Verwendung:

    //main.c
    
        PipelineManager<std::string> *man = PipelineFactory<std::string>::CreateFromConfig(pipelinename,file);
    
        //SampleFilter f;  
    
        std::string data("thats a test string");
        man->executeChain(&data); //findet den Samplefilter nur wenn Zeile 5 aufgerufen wird.
    

    Das Problem ist offensichtlich. Die Klasse SampleFilter wird nie verwendet vor dem Aufruf von executeChain().
    Deshalb kann sie sich auch nicht in der FilterMap registrieren.
    Könntest du vielleicht an meinen Code andeuten wie ich deine Lösung in meinen Code integrieren kann?

    LG und vielen Dank nochmal,
    Daniel


Log in to reply