Templateklasse soll je nach Typ unterschiedliche Member-Funktionen aufrufen



  • Servus,

    Eine Templateklasse:

    .h-Datei:

    template <typename T>
    class A{
    public:
    //blabla    A(qint8 pid, const QString& name, const QString&  DataRef, int crit = Crittype::Uncritical);
        A();
        ~A();
    
    /*blablabla */
        bool poll();
    private:
    /*blablabla*/
    };
    

    Soll die Instantiierung als A<int> und A<double> händeln können. Sie wird nie anders instantiiert (kann man das irgendwo festlegen? In Java gibts sowas schönes wie T extends Basistyp oder ähnliches...).

    Jetzt muss die Funktion poll() ein ganz kleines bißchen anders arbeiten wenn ein int gehandelt wird statt einem double.

    Daher Implementierung:
    .cpp-Datei:

    #include "flightstatusdata.h"
    #include <iostream.h>
    
    template <typename T>
    A<T>::A():
            m_id(0),
            m_data(0),
            //blablabla
    {
    }
    
    template <>
    bool A<int>::poll()
    {
        cout << "Called with int";
        return (// Aktion die fuer int gemacht werden muss);
    }
    template <>
    bool A<double>::poll()
    {
        cout << "Called with double";
        return (....);//Aktion die fuer double gemacht werden muss
    }
    

    Das lässt mir der Compiler (GCC 4.2) durchgehen, aber der LINKER nicht.

    main.cpp:

    #include "flightstatusdata.h"
    #include <iostream>
    int main()
    {
    	A<int>* fs = new A<int>();
    	fs-> poll();
    	delete fs;
    	fs=0;
    	std::cout << "Finish";
    }
    

    Meldung von ld:

    ./src/main.o: In function `A<double>::poll()':
    /home/philipp/.eclipse/test/Debug/../src/a.cpp:89: multiple definition of `A<double>::poll()'
    ./src/a.o:/home/philipp/.eclipse/test/Debug/../src/a.cpp:89: first defined here
    ./src/main.o: In function `A<int>::poll()':
    /home/philipp/.eclipse/test/Debug/../src/a:82: multiple definition of `A<int>::poll()'
    ./src/a.o:/home/philipp/.eclipse/test/Debug/../src/a.cpp:82: first defined here
    collect2: ld returned 1 exit status
    

    Ich weiß, da ich von Java komme hab ich für Template-C++ viele falsche Angewohnheiten.

    Dennoch wäre ich dankbar, wenn mir mal jemand die Tomaten von den Augen nehmen könnte und mit sagt, wie ich das zum Laufen bekomme.

    TIA!



  • PhilippM schrieb:

    Soll die Instantiierung als A<int> und A<double> händeln können. Sie wird nie anders instantiiert (kann man das irgendwo festlegen? In Java gibts sowas schönes wie T extends Basistyp oder ähnliches...).

    das hatten wir erst vor kurzem in einem anderen Thread. ist nicht sinnvoll. Der compiler hält dich schon auf, wenn du was dummes machst. Und siehs mal so: wenn du die Klasse eh nicht anders verwenden willst, warum dann sorgen machen? das Problem löst sich wie so vieles von selbst, wenn man nichts tut. Und du musst nicht mit den Kopf gegen die Wand schlagen, wenn sich doch mal was ändert.

    Dein Problem ist, dass du nur eine spezialisierung für eine methode willst. das geht in C++ aber nicht. Du musst immer die komplette Klasse spezialisieren. Das nächste problem ist, dass du nur spezialisierungen hast. Der linker wird später nochmal meckern, dass es keine Allgemeingültige implementierung von poll gibt.

    Also du musst dich entscheiden, welche der beiden Funktionen die "normale" abarbeitung sein soll, und dann die Klasse nochmal für den anderen typen (zum Beispiel double) implementieren.

    Oder du schreibst poll um:

    template<class T>
    struct DoPoll
    {
        static bool doPoll(/*hier alles benötigte eintragen*/)
        {
             //generelle funktion
        }
    };
    template<>
    struct DoPoll<double>
    {
        static bool doPoll(/*hier alles benötigte eintragen*/)
        {
             //funktion für double
        }
    };
    
    ...
    template <typename T>
    class A{
    public:
        bool poll()
        {
             return DoPoll<T>::doPoll(/*argumente*/);
        }
    };
    


  • Danke für den Input!

    otze schrieb:

    Also du musst dich entscheiden, welche der beiden Funktionen die "normale" abarbeitung sein soll, und dann die Klasse nochmal für den anderen typen (zum Beispiel double) implementieren.

    Äh, so:

    template <typename T>
    bool FlightStatusData<T>::poll()
    {
        cout << "Called with not-double";
    //Tu was
    }
    template <>
    bool FlightStatusData<double>::poll()
    {
        cout << "Called with double";
    // Tu was anderes
    }
    

    ??

    Das geht auch nicht. Die andere Lösung sieht zwar auch gut aus, ist mir aber zu umständlich, wenn auch das geht, was ich zitiert habe.



  • Ne sorry, das funktioniert. Es war irgendein Compiler der das nicht wollte. hab das grad nochmal nachgeschaut:
    post aus comp.lang.c++.moderated

    deine Lösung sollte funktionieren. Die Lösung von oben brauchst du, wenn die methode selbst ein template ist.(Die template regeln sind viel zu komplex btw)



  • otze schrieb:

    deine Lösung sollte funktionieren. Die Lösung von oben brauchst du, wenn die methode selbst ein template ist.(Die template regeln sind viel zu komplex btw)

    Tut sie leider nicht. Quellcode wie oben, Fehlermeldung:

    In function `A<double>::poll()':
    /home/philipp/.eclipse/test/Debug/../src/a.cpp:77: multiple definition of `A<double>::poll()'
    ./src/a.o:/home/philipp/.eclipse/test/Debug/../src/a.cpp:77: first defined here
    collect2: ld returned 1 exit status
    

  • Mod

    Da es sich um nicht um Templatedefinitionen sondern um Funktionsdefinitionen handelt, ist nat. die ODR entsprechend zu beachten. Also die Definitionen aus dem Header raus (dort müssen dann aber die Spezialisierungen weiterhin deklariert werden, um die implizite Instantiierung zu unterdrücken!). Oder man macht die Funktionen schlicht inline.
    Korrektur: die SPezialisierungen müssen nur dann im Header deklariert werden, wenn die Memberunktion bereits unspezialisiert definiert wurde.



  • camper schrieb:

    Da es sich um nicht um Templatedefinitionen sondern um Funktionsdefinitionen handelt, ist nat. die ODR entsprechend zu beachten. Also die Definitionen aus dem Header raus (dort müssen dann aber die Spezialisierungen weiterhin deklariert werden, um die implizite Instantiierung zu unterdrücken!). Oder man macht die Funktionen schlicht inline.
    Korrektur: die Spezialisierungen müssen nur dann im Header deklariert werden, wenn die Memberunktion bereits unspezialisiert definiert wurde.

    Ähh?
    Das verstehe ich jetzt nicht. Diese Definitionen

    template <typename T>
    bool FlightStatusData<T>::poll()
    {
        cout << "Called with not-double";
    //Tu was
    }
    template <>
    bool FlightStatusData<double>::poll()
    {
        cout << "Called with double";
    // Tu was anderes
    }
    

    stehen im .cpp-File, nicht imn h-file.

    Was muss ich aus dem h-file rausnhemen/ hinzufügen?


  • Mod

    Zeig mal den relevanten Code, einschließlich der Angabe was wo inkludiert wird.



  • camper schrieb:

    Zeig mal den relevanten Code, einschließlich der Angabe was wo inkludiert wird.

    Also Header flightstatusdata.h:

    #ifndef FLIGHTSTATUSDATA_H
    #define FLIGHTSTATUSDATA_H
    
    #include "XPLMDataAccess.h"
    #include "crittype.h"
    
    #include <QtGlobal>
    #include <QString>
    
    template <typename T>
    class FlightStatusData{
    public:
        FlightStatusData(qint8 pid, const QString& name, const QString&  DataRef, int crit = Crittype::Uncritical);
        FlightStatusData();
        ~FlightStatusData();
        qint8 id();
        T data();
        bool poll();
    /*weiteres Zeug..*/
    private:
        bool changeValue(T data);
        T m_data;/*weiteres Zeug*/
    };
    

    cpp-file:

    #include "flightstatusdata.h"
    
    #include "XPLMDisplay.h"
    #include "XPLMUtilities.h"
    #include "XPLMProcessing.h"
    #include "XPLMDataAccess.h"
    
    #include "config.h"
    #include <iostream>
    
    template <typename T>
    FlightStatusData<T>::FlightStatusData():
            m_id(0),
            m_data(0),
            m_name(""),
           // weitere Initialisierungen
    {
    }
    
    template <typename T>
    FlightStatusData<T>::FlightStatusData(qint8 id, const QString& name, const QString& dataRef, int crit):
            m_id(id),
            m_data(0),
     // einige weitere initialisierungen
    {
    }
    
    template <typename T>
    FlightStatusData<T>::~FlightStatusData()
    {
    }
    
    template <typename T>
    qint8 FlightStatusData<T>::id()
    {
        return m_id;
    }
    
    template <typename T>
    T FlightStatusData<T>::data()
    {
        return m_data;
    }
    
    // usw usw ....
    
    //jetzt kommt der casus knacksus:
    
    template <typename T>
    bool FlightStatusData<T>::poll()
    {
        std::cout << "Called with not-double, probably int";
        return changeValue((int)XPLMGetDatai(m_defDataRef));
    }
    template <>
    bool FlightStatusData<double>::poll()
    {
        std::cout << "Called with double";
        return changeValue((double)XPLMGetDataf(m_defDataRef));
    }
    
    // weitere private Funktionen ...
    

    Main - Zwecks testen:

    #include "flightstatusdata.h"
    #include <iostream>
    
    int main()
    {
    	FlightStatusData<int>* fs = new FlightStatusData<int>();
    	fs-> poll();
    	delete fs;
    	fs=0;
    	std::cout << "Finish";
    }
    


  • Du mußt alle Template-Definitionen in der Header-Datei definieren (bzw. von dort aus eine Datei inkludieren), jedoch die Definitionen der Spezialisierungen gehören (wegen der ODR) in eine CPP-Datei.

    // in Header-Datei (direkt oder indirekt)
    template <typename T>
    FlightStatusData<T>::Method(...)
    {
    }
    
    // in Source-Datei
    template <>
    bool FlightStatusData<double>::poll()
    {
    }
    

    Die Fehlermeldung des Linkers deutet irgendwie darauf hin, als ob du die CPP-Datei mehrmals eingebunden (inkludiert) hast.

    Edit: Rechtschreibung...



  • Hey, klasse. Dankeschön.

    Hab jetzt alle unspezialisierten Funktionen in eine Datei *.impl gepackt und diese an das Ende des Headers includiert.
    Die spezialiserten Funktionen stehen in der .cpp und jetzt fluppts!

    Is schon schlimm wenn man es anders gewöhnt ist. Son Chaos gibt's in Java nicht. Da gibt's halt Genrics und keine Templates. Von der Idee her ähnlich, aber eben doch von der Implementierung was völlig anderes.

    Dankeschön an alle die zur Lösung des Problems beigetragen haben.


  • Mod

    PhilippM schrieb:

    Hey, klasse. Dankeschön.

    Hab jetzt alle unspezialisierten Funktionen in eine Datei *.impl gepackt und diese an das Ende des Headers includiert.
    Die spezialiserten Funktionen stehen in der .cpp und jetzt fluppts!

    Vergiss dann aber nicht, die Spezialisierungen in der *.impl-Datei zu deklarieren. Eine andere Möglichkeit als die Implementation im Header (bzw. .impl - was auf das Gleiche hinausläuft) besteht dann, wenn alle möglichen Tamplateargumente bekannt sind. Dann kann man die Implementation auch wie gewohnt in der .cpp-Datei stehen lassen und dort für alle Argumente instantiieren.


Anmelden zum Antworten