Template Frage - design: Input-Anhängig?



  • Du wirst ja auch im ganzen restlichen Programm (oder in einem vielleicht nicht so kleinen Teil davon) mit der complex-liste arbeiten oder mit der double-liste, je nachdem. Du koenntest einen abstrakten Listenverarbeiter definieren und von dem die konkreten Verarbeiter ableiten, die dann die Typspezifischen Einzelheiten implementieren:

    class AbstractListVerwurster {
    public:
      virtual ~AbstractListVerwurster() {} = 0;
      virtual void input(std::istream& is) = 0;
      virtual void output(std::ostream& os) = 0;
    };
    

    Fuer weitgehend normale Daten kannst du dann ein Klassentemplate definieren, das davon ableitet:

    template <typename T>
    class EasyListVerwurster : public AbstractListVerwurster {
    public:
      virtual void input(std::istream& is) { 
        //standard eingabeprozedur
      }
      virtual void output(std::ostream& os) {
        //standard ausgabeprozedur
      }
    private:
      std::list<T> list_;
    };
    

    Und im Programm dann:

    AbstractListVerwurster *wurstl;
    if (/*Dateiinhalt ist double*/) wurstl = new EasyListVerwurster<double>;
    else if (/*Dateiinhalt ist complex*/) wurstl = new EasyListVerwurster<complex>;
    
    wurstl->input(datei);
    wurstl->output(std::cout);
    

    Wenn du das Ganze auch noch fuer eine selbstgeschriebene KLasse machen moechtest, wo z.B. das Einlesen udn die Ausgabe nicht so einfach sind, kannst du
    a) Eine Spezialisierung EasyListVerwurster<MySpecialClass> definieren oder
    b) Eine andere Verarbeiterklasse von AbstractListVerwurster ableiten - die Moeglichkeiten auch fuer verschiedene Verarbeitungsmethoden deiner doubles sind quasi unbegrenzt.



  • Danke für eure großzügige Hilfe!

    Also prinzipiell werde ich lediglich 2 Inputs haben - wie schon gesagt: double und complex also 2 double werte.

    Prinzipiell läuft der Algorithmus mit allen funktionen dann identisch ab, nur an einigen wenigen stellen werden dann halt 2 double werte übergeben wenns complex ist bzw. nur ein double wert wenns real ist.

    Ich glaube overloading würde den code ziemlich aufblähen und fast doppelt halten dann oder?

    Meine Frage ist auch: Polymorphie ist an dieser stelle nicht unbedingt nötig oder? Weil ich weiß ja welcher input dann kommen wird und dann alloziiere ich halt abhängig z.B nicht ein double* arr = new double, sondern halt ein complex* arr = new complex oder sowas....



  • testo schrieb:

    Meine Frage ist auch: Polymorphie ist an dieser stelle nicht unbedingt nötig oder? Weil ich weiß ja welcher input dann kommen wird und dann alloziiere ich halt abhängig z.B nicht ein double* arr = new double, sondern halt ein complex* arr = new complex oder sowas....

    Schon, nur musst du dann beim Einlesen die unterscheidung machen, bei Ausgaben die unterscheidung machen, eventuell bei irgendwelchen Rechnungen die Unterscheidung machen usw. Du kannst nichtmal deine Bibliotheksfunktionen ohne if-else staement aufrufen, weil du ja vermutlich die Daten in einen Container liest und dann jeweils entsacheiden musst, in welchem der beiden Container (die auch noch nebeneinander existieren muessten) diene Werte nu eigentlich drin sind.
    Du musst die ganze Zeit ueber die Typunterscheidung zu fuss machen, waehrend polymorphie doch gerade dazu da ist 🙂



  • 🙄
    ich habe jetzt noch genauer hingeschaut in meinem code....es wird sich im endeffekt nur ein array ändern - klar das was die werte trägt
    ich habe eine Klasse Matrix die ein Pointer auf eine Klasse Lines hat. Lines hat jetzt das array das die werte trägt vals. vals wird also entweder ein double array sein oder eben ein array mit elementen die ein struktur complex sein werden.

    Ich übergebe immer die Matrix als pointer und greife auf die werte so zu:
    Matrix->lines->vals[i];

    wenn ich jetzt z.B einen teil in ein anderes array kopieren wollte - muss ich ja das neue array vom gleichen typ anlegen und auch die methode die den Pointer Matrix bekommt muss ja dann intern abhängig vom typ unterschiedlich arbeiten.

    Was wäre jetzt die korrektere/einfachere Lösung?



  • Also ich poste mal nen einfachen beispielcode der von der struktur her ähnlich ist:

    //main:
    Matrix *A = NULL;
    
    Read::read(A);
    
    Compute::algorithm(A);
    
    Read::read(Matrix *&A)
    {
       //liest input file in Datenstruktur A ein:
       A->anzahl_zeilen = anzahl_zeilen_aus_file;
       foreach(//zeile in file)
           A->lines->val[i] = double_aus_file
    }
    
    Compute::algorithm(Matrix *&A)
    {
         double * arr = NULL;
          arr = copy_to_arr(A);
    
        //Jetzt kommt der bibliotheksaufruf der abhängig ist von dem typ
        if (//type = double)
              bibl_1(arr);
        else //type = complex
              bibl_2(arr);
    }
    
    double*
    Compute::copy_to_arr(Matrix *&A)
    { 
          double *arr = new double[dim];
    
          //kopiere einen teil aus A->lines->val nach arr
    
          return arr;
    }
    
    class Matrix
    {
         public:
                   Lines *lines;
                   int    anzahl_zeilen;
    };
    

    vals sollte dann je nach input nicht nur ein double wert enthalten sondern auch z.B die structur-elemente complex.

    und das würde ich gerne auf einen complex input erweitern, also so dass ich z.B auch in der Methode copy_to_arr() aus vals dann spezifisch halt die complex-werte auslesen kann.



  • hmm...ich bräuchte eigentlich schon noch hilfe...



  • Wenn ich dich richtig verstehe ist das relativ einfach zu lösen. Und zwar machst du einfach aus allen Programmteilen die mit den doubles umgehen Templates, und aus der main Funktion auch. Dort wo es Unterschiede gibt spezialisierst du einfach.
    Die eigentliche main Funktion guckt sich dann bloss das File an, und ruft je nachdem was drinnen steht die "double" oder "complex" Variante auf:

    void main()
    {
        file f(...);
        if (is_complex_input_file(f))
            my_main<std::complex<double> >(f);
        else
            my_main<double>(f);
    }
    

    Dadurch hast du bloss ein einziges "if" ganz zu beginn des Programms, alles andere ist statisch gebunden.
    Das führt zwar dazu dass im Prinzip der gesamte Code verdoppelt wird, dein Programm also deutlich grösser werden wird, aber das sollte schätze ich mal egal sein.



  • ....ja...danke - ich hoffe du hast dir meinen letzten post angeschaut? gäbe es evtl. irgendwie eine möglichkeit die verdopplung einzuschränken? aber auch wenn ich polymorph arbeiten würde hätte ich ja eine verdopplung, denn die methoden die polymorph wären müsste ich ja dann auch doppelt schreiben.

    Also prinzipiell macht das doppelte code nichts aus. Hauptsache ist dass es performant bleibt - was es ja durch eine einzige if-abfrage auch bleibt...

    für weitere Stellungnahmen bin ich gerne offen 🙂 Danke an dieser stelle



  • testo schrieb:

    ....ja...danke - ich hoffe du hast dir meinen letzten post angeschaut?

    Ja, hab' ich. Allerdings würde ich "read" und "algorithm" einfach als Template-Funktionen realisieren (nicht als Template-Klassen), vorausgesetzt sie sind "stateless". Und der Code sieht für mich nicht sehr sauber aus, deswegen bin ich nicht genauer darauf eingegangen 🙂

    gäbe es evtl. irgendwie eine möglichkeit die verdopplung einzuschränken?

    Ja, gibt es. Du kannst Teile die komplett unabhängig vom Typ sind als normale Funktionen implementieren, bzw. wenns um Memberfunktionen geht diese in eine Basisklasse schieben von der du die Template-Klasse dann ableitest. Oft geht das aber nur an wenigen Stellen (mit ganz kleinen Funktionen), und in dem Fall verzichtet man meist einfach darauf.

    aber auch wenn ich polymorph arbeiten würde hätte ich ja eine verdopplung, denn die methoden die polymorph wären müsste ich ja dann auch doppelt schreiben.

    Wenn du polymorph arbeitest ergibt sich oft eine Grenze, ab der es keine Verdoppelung mehr gibt. Wenn du z.B. ein Array aus Zeigern auf "comparable" sortierst (wobei comparable::is_less virtual ist und zum Vergleich verwendet wird), dann ist das eine einzige "sort_comparable" Funktion für alle comparable. Wenn du es über Templates machst sind es verschiedene Instanzierungen des "sort" Templates. Das macht oft einen riesen Unterschied.

    EDIT:
    polymorph wird also nur "is_less" verdoppelt.

    Mit Templates dagegen wird is_less, sort und jede Funktion die sort aufruft verdoppelt (da alle Funktionen die sort aufrufen wollen selbst Templates sein müssen, sonst könnten sie ohne runtime polymorphismus nicht mit unterschiedlichen Typen arbeiten).
    /EDIT

    Also prinzipiell macht das doppelte code nichts aus. Hauptsache ist dass es performant bleibt - was es ja durch eine einzige if-abfrage auch bleibt...

    Dann würde ich es über Templates machen und auf keinen Fall irgendwo Runtime Polymorphismus verwenden. Dass das Programm dadurch grösser wird wird in dem Fall nicht stören, da die (bei einem Durchlauf) nicht verwendeten Funktionen eben nicht verwendet werden, also nicht im Cache landen, und daher auch nicht "bremsen" können.

    für weitere Stellungnahmen bin ich gerne offen 🙂 Danke an dieser stelle

    Büdde gerne 🙂



  • puh...also es funktioniert schon so....aber ich glaube ich muss sehr sehr viel code doppelt schreiben.

    also ich versuche ein einfaches beispiel und würde gerne wissen ob es keinen weg daran vorbei gibt:

    ich habe eine template class. In dieser klasse wird abhängig vom input z.B ein array gefüllt mit objekten.

    template <class T>
    class my_class
    {
        T* arr;
    }
    

    wenn jetzt im input file z.B nur reelle werte stehen , dann soll das array mit doubles gefüllt werden , es wird also ein objekt mit my_class<double> erstellt.

    falls z.B complex der input ist dann halt ein objekt my_class<complex> erstellt.

    jetzt muss ich aber den kompletten algorithmus doppelt schreiben weil ja die datenstruktur unterschiedlich. ist. ALso überall wo z.B auf das arr zugegriffen wird muss der code ja doppelt sein da T immer anders ist.
    Also z.B um die werte aus arr auszugeben muss ich 2 Methoden schreiben die z.B nur double werte ausgeben oder aus der complex - structure die jeweils 2 double werte rausholen.

    Stimmt dieses vorgehen bzw. meine idee/implementierung soweit?


Anmelden zum Antworten