Geht das eleganter?



  • SucherDerEleganz schrieb:

    Bei Leuten die ernsthaft jetzt noch immer nicht meine simple Frage verstanden haben, muss ich wirklich an deren Intelligenz zweifeln....

    Kannst Du natürlich machen - aber Du kannst auch kurz in Erwägung ziehen, dass vielleicht wirklich Deine Fragestellung doch nicht gaaaaanz so eindeutig und allgemeinverständlich ist, wie Du Dir das gedacht hast.
    So wie beim "Geisterfahrerphänomen" nicht unbedingt wirklich alle Anderen falsch liegen müssen ....

    Vermutlich bist Du noch nicht so lange hier im Forum, aber es treiben sich hier (auch in diesem Thread) sehr gute Leute rum (ich zähle mich nicht dazu).

    ... und spätestens wenn camper, der den C++-Standard rückwärts auswendig draufhat und bei dem ich noch NIE erlebt habe (und ich bin nun auch schon eine längere Zeit hier dabei), dass er eine Programmieraussage revidieren musste, die Fragestellung nicht versteht, würde ich das schon sehr ernst nehmen.
    Aber auch das ist Deine freie Entscheidung.

    Letztlich bleibt's dabei: Wenn diesen Augenblick das Forum für immer abraucht, hast Du weiterhin Dein Problem - und sonst keiner von uns hier. 😃

    So - und nu geh' ich ins Bett zu meinem lieben Frauchen.

    Nacht,

    Simon2.



  • Simon2 schrieb:

    ... und spätestens wenn camper, der den C++-Standard rückwärts auswendig draufhat und bei dem ich noch NIE erlebt habe (und ich bin nun auch schon eine längere Zeit hier dabei), dass er eine Programmieraussage revidieren musste, die Fragestellung nicht versteht, würde ich das schon sehr ernst nehmen.

    Nicht zu viel loben, sonst strengt er sich nicht mehr an. 😃
    Also, ich habs schon erlebt. 😉

    Trotzdem, es stimmt schon. Der Thread ist beinahe eine Elefantenrunde. 😃



  • Simon2 schrieb:

    Diese Anforderung ist mir bei Dir bislang noch nicht aufgefallen.

    Ihr wollt mich doch verarschen, oder? So eine Art Aprilscherz. Auf SEITE 1 des Threads ist in meinem Code Sample klar ersichtlich, dass void* data; ein MEMBER der Klasse ist und ich somit also die eingelesenen Daten in der Klasse halten will.

    Simon2 schrieb:

    Die wesentliche Frage hier ist: Befinden sich in Deiner Containerinstanz zu einem Zeitpunkt immer Objekte gleichen Typs ?

    Auch das ist auf Seite 1 klar geklärt. Spätestens mit dem Posting von Badestrand sollte das eindeutig gewesen sein:

    Oh, sorry, dann hatte ich das falsch verstanden. Ich dachte, es würden gemischt floats, ints usw drinstehen.

    Aus dem Posting ist klar ersichtlich, dass ich KEINE Mischarrays oder sowas brauche. Und dann sagst du mir ein paar Seiten weiter, dass das nicht klar hervorging. Sorry, aber...

    Weil das Problem ja immernoch so völlig diffus und undurchschaubar zu sein scheint ( 🙄 ) hier nochmal (ca. zum 3. Mal) der Code wie es im Moment aussieht (vereinfacht):

    class Foo {
    private:
       void*		data;
       DATA_TYPE		dataType;
    public:	
       Foo(DATA_TYPE dataType) { 
          // stream öffnen usw
          this->dataType = dataType;
          if(dataType == RAW_F32) {
             data = new FLOAT[count]; 
             stream.read( (char*)data, count * 4);
          }
          else if(dataType == RAW_U8) {
             data = new BYTE[count]; 	
             stream.read( (char*)data, count);
          }
       }
    
       // In genauer EINER einzigen Methode greife ich nun lesend auf das Array zu:
    
       void method() {
          // ...
       if(dataType == RAW_F32)
          v[index].SplineB.y = (static_cast<FLOAT*>(data))[index] * this->scaleY;
       else if(dataType == RAW_U8)
          v[index].SplineB.y = (static_cast<BYTE*>(data))[index] * this->scaleY;	
    
       }
    

    Das ist alles. Wie ich es auf mehreren Seiten immer wieder gepostet habe. Und jetzt nochmal meine simple Frage (zum 2. oder 3. Mal): Bei euren Lösungen, durch welchen Datentyp ersetze ich nun void* data (da ich ja die Daten als Membervariable in der Klasse halten will)?



  • Simon2 schrieb:

    So - und nu geh' ich ins Bett zu meinem lieben Frauchen.

    Ich habe mich dich immer jünger vorgestellt. 🙂 (Was wohl am namen liegt, da ich nur jugendliche Simons kenne. :))

    @SucherDerEleganz :
    Ist DATA_TYPE zur Laufzeit bekannt?
    Wenn ja: Siehe mein Beispiel auf Seite 1
    wenn nein: Siehe mein Kommentar auf Seite 2. (virtuelle Funktionen)

    class IDataType
    {
    public:
        virtual void read () = 0;
        virtual IDataType (){}
    };
    
    class RAW_F32 : public IDataType
    {
    public:
        void read () {}
    };
    
    class RAW_U8 : public IDataType
    {
    public:
        void read () {}
    };
    ...
    
    class Foo {
    private:
       std::list<IDataType*>   data;
    public:   
       Foo(std::string type) {
          if ( type == "RAW_F32")
              data.push_back (new RAW_F32);
          else if ( type == "RAW_U8")
              data.push_back (new RAW_U8);
          ...
       }
    
       void method() { 
       //gemütlich auf den Container zugreifen und die Schnittstelle benutzen und du hast keine Probleme.
       }
    

    Allerdings vermute ich, dass die Typen bereits zur Compilezeit bekannt sein können, wenn du da ja sowas hast: DATA_TYPE dataType. Aber wie auch immer. Das, was ich jetzt gesagt habe haben schon andere vor mir (+ ich selbst) schon gesagt.. 🙄



  • drakon schrieb:

    ...
    Ich habe mich dich immer jünger vorgestellt. 🙂 (Was wohl am namen liegt, da ich nur jugendliche Simons kenne. :))...

    Um das mal klarzustellen: Ende des Jahres werde ich 40, bin seit 13,5 Jahren verheiratet und habe zwei 10jährige Töchter (also insgesamt 3 großartige Mädels im Haus, die mir immer wieder Gelassenheit schenken für so Heißsporne wie den Threadersteller hier 😉 😃 ).

    Ich kenne sonst auch nur recht jugendliche "Simons" und war meine ganze Kindheit hindurch eigentlich ganz stolz auf meinen Exotennamen (es gab sogar Leute, die fragten, ob ich jüdisch sei). Dafür habe ich leider auch nicht das "Überhören" gelernt, wenn Namensvettern gerufen werden, wie es wohl Franks, Thomasse und Claudias entwickelt haben. Wenn jetzt ein Mutter ihren "Siiiiiiimon" ruft, komme ich auch oft angelaufen (tja, ich weiß schon, warum Elten ihren Kindern peinlich sind 😉 ).

    Gruß,

    Simon2.



  • SucherDerEleganz schrieb:

    Simon2 schrieb:

    Diese Anforderung ist mir bei Dir bislang noch nicht aufgefallen.

    ...Auf SEITE 1 des Threads ist in meinem Code Sample klar ersichtlich, dass void* data; ein MEMBER der Klasse ist...

    Aha! Ich kann Dir ach genau sagen, wo mein Mißverständnis lag:
    Lies mal laut:

    SucherDerEleganz schrieb:

    ...Ich brauche ja einen Container/Array als Member, das mir die Daten aus der Datei hält ...

    (so habe ich es verstanden)
    und

    SucherDerEleganz schrieb:

    ...Ich brauche ja einen Container/Array als Member, das mir die Daten aus der Datei hält ...

    (so hast Du es anscheinend gemeint)
    Das sollte ein wenig belegen, warum man schriftlich etwas ausführlicher formulieren muss als in der "Sprechsprache".

    @Topic:
    1.) Ich sehe nicht, wo meine Lösung einem Einsatz innerhalb einer Klasse widersprechen sollte. Dann packst Du die Funktionen eben in eine Klasse, aber das bekommst Du schon hin. Du kannst Dich auch noch entscheiden, ob Du für jeden Typ einen eigene Klasse anlegen (sprich: Klassentemplate) oder innerhalb der einen Klasse die "fachlichkeit()" typspezifisch machen möchtest (wie ich es oben getan habe)
    2.) Ich sehe auch nicht, warum diese Anforderung besteht; die Implementierung in nerhalb einer Klasse ist ein eher technisches Implementierungsdetail, das mit den fachlichen Anfordeungen eher weniger zu tun hat.

    Aber nun gut:
    Entwurf mit Klassentemplate (1.)

    template <typename T>
    struct foo {
       vector<T> v;
       void fachlichkeit() {
          y = v[index] * scaleY; // keine Fallunterscheidung mehr nötig
       }
       foo(size_t) : v(size) {}
    };
    
    int main() {
    ...
       switch(dataType) {
       case TYPE_BYTE: foo<char>(X).fachlichkeit(); break
       case TYPE_FLOAT: foo<float>(X).fachlichkeit(); break
    ...
    

    (Kann natürlich auch erstmal nur das foo-Objekt erzeugen (mit new und interface wie von Nexus beschrieben) und dann woanders fachlichkeit() aufrufen)

    ... und einer für 2.)

    struct foo {
       size_t s;
       template <typename T>
       void fachlichkeit() {
          vector<T> v(s);
          y = v[index] * scaleY; // keine Fallunterscheidung mehr nötig
       }
       foo(size_t) : s(size) {}
    };
    
    int main() {
    ...
       foo f(X);
       switch(dataType) {
       case TYPE_BYTE: f.fachlichkeit<BYTE>(); break
       case TYPE_FLOAT: f.fachlichkeit<FLOAT>(); break
    ...
    

    Ein Pferdefuß von Nexus' reiner "virtual-Lösung" ist, dass nicht generisch auf die Daten in der Liste zugegriffen werden kann. Ich weiß zwar, dass alle ein "idata"-Objekt beinhalten, aber wenn ich konkret mit einem arbeiten möchte, weiß ich nicht, welchen Typ sie haben ... und eine "generische Zugriffsfunktion" kann es auch nicht geben aufgrund der "Vererbungssemantik" (=gleiche Funktionssignatur) in C++.
    Aber man könnte sie mit meinen Ansätzen kombinieren.

    Nochmal ein Tipp zu Deiner Kommunikation: Wenn Du
    1.) Nicht sofort verbal um Dich schlägst, wenn Dir etwas nicht passt
    2.) eigene Begrenztheit (wie sie jeder Mansch hat) und die Existenz von Mißverständnissen ("Warum Boshaftigkeit voraussetzen, wo Irrtum oder DUmmheit es genauso erklären?", sagt ein Kollege immer so schön) mit in Deine Überlegungen einbeziehst und
    3.) Dich nicht nur auf die Dinge, die Dir nicht passen stürzt, sondern auch mal sagst, wo Du was verstehst, was Dir gefällt, ....
    dann kommst Du im Leben, im Internet und erst Recht hier im Forum deutlich schneller und stressfreier zum Ziel (ich habe nämlich deutlich mehr geschrieben als die paar Schnipsel, auf die Du eingehst).

    So - nun bringe ich meine Tochter zum Volleyballspiel.

    Schönen Tag noch,

    Simon.



  • Ein Pferdefuß von Nexus' reiner "virtual-Lösung" ist, dass nicht generisch auf die Daten in der Liste zugegriffen werden kann. Ich weiß zwar, dass alle ein "idata"-Objekt beinhalten, aber wenn ich konkret mit einem arbeiten möchte, weiß ich nicht, welchen Typ sie haben ... und eine "generische Zugriffsfunktion" kann es auch nicht geben aufgrund der "Vererbungssemantik" (=gleiche Funktionssignatur) in C++.

    Ich denke mal, dass du mich gemeint hast und nicht Nexus. 😉

    Das mit dem Zugriff ist mir schon klar, aber im Normalfall tendieren die Leute dazu zu meinen, dass sie wissen müssen, mit was sie es zu tun haben, als sich einfach auf die Schnitstelle zu verlassen.
    Schlussendlich macht man das ganze mit abstrakter Klasse usw. ja darum, weil die Objekte eine einheitliche Aufgabe haben, aber jeweils ev. etwas anderes machen müssen. Wenn aber keine einheitliche Schnittstelle gefunden werden kann, dann hat auch keine Vererbung stattzufinden.
    (Nichtsdesto trotz ist deine Lösung natürlich eine gute Möglichkeit das zu erzwingen. Nur muss man sich dann fragen, ob das so nötig ist. Und mir fällt kein gutes Beispiel ein, wo es das wirklich ist. 😉 )



  • drakon schrieb:

    ...Ich denke mal, dass du mich gemeint hast und nicht Nexus. ;)...

    😮 😮 😮
    KREISCH!!
    Natürlich - sorry.

    drakon schrieb:

    ...Das mit dem Zugriff ist mir schon klar, aber ...

    Hast natürlich Recht, aber ich wollte den Threadstarter da vorsichtshalber mal darauf hinweisen. Mir schien es halt so, als ob er die Klasse im Wesentlichen als "Verpackung für seine Daten" haben, deren eigentliche Verarbeitung aber außerhalb haben wollte. Und dahin kommt er eben mit Deinem Ansatz nicht (ungefrickelt)...
    Letztlich wollte ich nur darauf hinweisen, dass da noch Arbeit vor ihm liegt, wenn er diesen Weg beschreitet.

    Gruß,

    Simon2.



  • Simon2 schrieb:

    int main() {
    ...
       switch(dataType) {
       case TYPE_BYTE: foo<char>(X).fachlichkeit(); break
       case TYPE_FLOAT: foo<float>(X).fachlichkeit(); break
    ...
    

    Ich habe nicht den ganzen Thread verfolgt. Aber bei dem obigen Programmcode stellen sich mir ein paar Fragen

    • Es geht nicht um das Double Dispatch Pattern?
    • Wir sind im C++ Forum?

    Wenn beide Fragen mit "Ja" zu beantworten sind, stellt sich gleich Frage Nummer drei:

    • Darf ich die Kotztüte hervorholen?


  • ~john schrieb:

    • Es geht nicht um das Double Dispatch Pattern?
    • Wir sind im C++ Forum?
    • Darf ich die Kotztüte hervorholen?

    Vielleicht, ja, wenn du es umbedingt willst...

    Ich bin noch immer der Auffassung das dieser Thread nichts mit Eleganz zu tun hat, und das wir - weil wir nicht die Hintergründe vom OP kennen, nur hier rätseln können. Vermutlich könnte man das alles deutlich eleganter lösen wenn wir nicht nur die Vorgabe wie der Code besser wäre, sondern auch das Umfeld kennen würden (Ich tippe nach wie vor an einen Designfehler, oder zumindestens auf etwas das man - zumindestens in C++ anders angeht).

    cu André



  • ~john schrieb:

    ...Darf ich die Kotztüte hervorholen?...

    Keine Sorge. Da Frage 1 bereits mit Nein zu beantworten ist, stellt sich diese hier nicht mehr.
    Es geht hier um einen einfachen Dispatch (reine Erzeugung) anhand von Typinformationen, die erst zur Laufzeit aus einer Datei gelesen werden.

    Das Lesen den ganzen Threads wäre vllt. keine schlechte Idee gewesen. :p 😉 😃
    (dann wäre vllt. auch klar geworden, dass es mir im Wesentlichen um die Möglichkeiten von templates und ihre Verknüpfung mit Laufzeitbedingungen ging)

    Gruß,

    Simon2.



  • Ganz ehrlich, alle Vorschläge sind mit Kanonen auf Spatzen schießen.
    Den ganzen von Simon2 geposteten Code kann ich so nicht verwenden. WIE (in welcher DATENSTRUKTUR - sprich: was nun als Member statt dem void* stehen soll) ist mir noch immer nicht klar.

    Dann der Code von drakon. Er ist wenigstens der einzige, der mal konkret sagt durch was ich nun void* ersetzen soll. Aber für jeden Datentyp eine eigene Klasse ableiten? Nur weil ich aus einer Datei Daten einlesen will? ...
    Und der Sinn von dem Code ist auch zweifelhaft. Du legst eine liste von IDataType* an. Wieso eine Liste, wenn du eh nur 1 Objekt reinlegst. Und wenn ich diese liste dereferenziere, dann bekomm ich ein IDataType*. Was soll ich damit? Ich will einfach v[index] schreiben und dann den richtigen Typen haben.

    Ich wollte einfach für diesen kleinen Problembereich (void* ersetzen bei verschiedenen Typen) eine simple Lösung - irgend ne andere Struktur oder sonst irgendwas mit 2-10 Zeilen. Offenbar ist das nicht möglich. Naja, drauf geschissen, das sind jetzt 3 Zeilen Code und ich lass den void*.

    Und so arrogante Aussagen wie sie von ~john könnt ihr euch auch sparen. Thread nicht mal lesen, falschen Vorschlag machen und zusätzlich auch arrogant von oben herab urteilen. Mit Double Dispatching hat das hier herzlich wenig zu tun - hier geht es im Kern nicht um Dynamisches Dispatching von Funktionen sondern um die richtigen Datenstruktur.

    Es gibt in C++ in vielen Fällen bessere Möglichkeiten als void*, aber es gleich generell zum "kotzen" finden, ist lächerlich. Als ob alles immer gut oder schlecht sein muss. Wenn der Code perfekt arbeitet und tief in einer lowen Klasse weggekaselt ist die man nie mehr anrühren muss, ist das absolut ok.



  • Also ich hab mir nicht den ganzen Thread durchgelesen, aber ich würde das vielleicht so machen:

    class IFoo
    {
    public:
        virtual void Bar() = 0; // die "eine Funktion" wo der Datentyp einen Unterschied macht
    };
    
    template <class T> class Foo : public IFoo, private boost::noncopyable
    {
    public:
        Foo(istream& is, size_t count)
        {
            // ...
            m_data.resize(count);
            is.read(reinterpret_cast<char*>(&m_data[0]), count * sizeof(T));
            // ...
        }
    
        virtual void Bar()
        {
            // ...
            // was auch immer "v" ist...
            v[index].SplineB.y = m_data[index] * m_scaleY;
        }
    
    private:
        std::vector<T> m_data;
        // ...
    };
    
    class VarFoo : private boost::noncopyable
    {
    public:
        template <class T> VarFoo(boost::type<T>, istream& is, size_t count) :
            m_foo(new Foo<T>(is, count))
        {
        }
    
    private:
        boost::scoped_ptr<IFoo> const m_foo;
    };
    

    Bei bloss 2 Datentypen ist das vermutlich mehr Code als die "if Orgien" Version, je mehr Datentypen es sind desto besser schneidet die Template-Version ab.
    Vor allem spart man sich die ganzen Fallunterscheidungen zu tippen, und vor allem auch zu warten. Wenig ist lästiger als eine 1-Zeilen änderung an 10 Stellen machen zu müssen weil man sinnlos Code dupliziert hat.



  • Und der Sinn von dem Code ist auch zweifelhaft. Du legst eine liste von IDataType* an. Wieso eine Liste, wenn du eh nur 1 Objekt reinlegst. Und wenn ich diese liste dereferenziere, dann bekomm ich ein IDataType*. Was soll ich damit? Ich will einfach v[index] schreiben und dann den richtigen Typen haben.

    Ich meine mal irgendwo was gelesen zu haben, dass das ein Array sein soll?! (OK. std::vector o.ä. wäre angebrachter gewesen.)

    Ansonsten, wenn du nicht zufrieden bist mit dem, was wir hier zeigen, dann belass es doch einfach bei void.. Die Sprachmittel sind ja schliesslich da, um genutzt zu werden. Und wenn du das Gefühl hast, dass void* das einzig richtige ist, dann kannst du das ja auch ruhig benutzen. (Was nicht bedeutet, dass ich das auch machen würde.)

    Ansonsten versuch doch mal ein exaktes Beispiel zu bringen, dass mit void* genau das macht, was du willst. (mit der dazugehörigen Datei zum einlesen usw.). Dann wissen wir nämlich genau, was du willst und können dir auch genau darauf eine Antwort geben. Ansonsten verschwendest du/wir nur unsere Zeit und am Schluss bist du nicht glücklich und wir haben uns einen Haufen Arbeit für nichts gemacht..



  • SucherDerEleganz schrieb:

    Ganz ehrlich, alle Vorschläge sind mit Kanonen auf Spatzen schießen. ...

    Äh - ich sehe hier keine "Kanonen".
    Der Code, der Dir hier vorgelegt wird, ist weder länger noch komplizierter noch langsamer noch .... als Deiner. Er nutzt lediglich mehr von den C++-Sprachmitteln (aber alles keine Zauberei).

    Und Dein Problem ist auch kein "Spatz".

    SucherDerEleganz schrieb:

    ...WIE (in welcher DATENSTRUKTUR - sprich: was nun als Member statt dem void* stehen soll) ist mir noch immer nicht klar....

    Das ist Dein Problem: Du willst kein anderes (eleganteres) Design, sondern lediglich einen "Programmierkniff", den Du bei Dir reinkopieren kannst, ohne grundlegend etwas zu ändern.
    Da allerdings die von Dir (zu Recht) beklagte "Uneleganz" kein kleiner Schönheitsfehler ("Spatz") ist, wirst Du das, was Du willst, nicht bekommen.

    Oder anders gesagt:

    SucherDerEleganz schrieb:

    ...Geht das eleganter?...

    Nein.
    (jedenfalls nicht unter den von Dir gemachten (und von mir größtenteils als unnötig empfundenen) Vorgaben)

    ... irgendwie habe ich den Eindruck, dass Du das eigentlich die ganze Zeit hören wolltest.

    Gruß,

    Simon2.



  • Man könnte zusammenfassend sagen:

    Ja, es geht eleganter. Aber solang du darauf bestehst dass alles so bleibt wie es ist, gehts eben nicht eleganter.



  • SucherDerEleganz schrieb:

    Und so arrogante Aussagen wie sie von ~john könnt ihr euch auch sparen.

    Arrogant ist hier nur eine Person. Es wird keinerlei Code herausgerückt, so daß es schlicht weg nicht möglich ist einen sinnvollen Vorschlag zu machen. Wenn man void* vermeiden will, muß man das Design entsprechend anpassen und dazu muß man das Programm entsprechend tiefgreifend ändern. Der Hinweis auf Double Dispatch ist insofern von Belang, weil dies der einzige sinnvolle Grund ist noch auf switch case mit TypeIDs in C++ auszuweichen. Macht man es trotzdem hat man einen Designfehler produziert. Bei Altlasten mag das durchaus sinnvoll sein es weiter zu behalten, aber das kann nicht der Regelfall für neuen Code sein.



  • pumuckl@logged_off schrieb:

    Man könnte zusammenfassend sagen:

    Ja, es geht eleganter. Aber solang du darauf bestehst dass alles so bleibt wie es ist, gehts eben nicht eleganter.

    Oder noch kürzer: "besser" ε { "anders" }
    😉



  • Was diskutiert ihr denn überhaupt mit so einer Knallerbse? Eure ganzen Tipps sind doch echt Perlen für die Säue. Ich wurde mal sagen, dass der gute™ TO sich inzwischen eindeutig als Troll™ geoutet hat.



  • Simon2 schrieb:

    Es geht hier um einen einfachen Dispatch (reine Erzeugung) anhand von Typinformationen, die erst zur Laufzeit aus einer Datei gelesen werden.

    Im Falle von zwei Typen ist ein "Dispatch" schon overkill, eine Fallunterscheidung tät es da schon. Wenn man sich allerdings die Mühe macht extra Klassen zu erzeugen und dann Switch&Case mit TypID benutzt, dann vergeht mir der Spaß. Denn in so einem Fall kann man das gleich sauber und elegant machen, d.h. mittels Polymorphie, dafür ist sie ja in C++ vorhanden.

    Simon2 schrieb:

    Das Lesen den ganzen Threads wäre vllt. keine schlechte Idee gewesen. :p 😉 😃

    Da der Thread nur wenig konkrete Information vom Urheber desselben beinhaltet, habe ich mich auf das Querlesen beschränkt. Ich weiß, bis jetzt nicht was sein konkretes Problem ist. Abgesehen davon, daß er Fallweise Floats und Doubles aus einer Datei einlesen muß, wird er nicht nämlich nicht sonderlich konkret.


Anmelden zum Antworten