Wie void pointer auf new struct umbiegen



  • Hi Folks

    Ich hab hier ein kleines Problem. Ich habe hier einen void Pointer

    void *frames;
    

    und dem möchte ich je nach Bedarf entweder ein

    struct sfm_key
    {
    	foo ...
    	void			*prev_frame;
    	void			*next_frame;
    };
    

    oder ein

    struct gfm_key
    {
    	foo ...
    	void			*prev_frame;
    	void			*next_frame;
    };
    

    zuweisen.

    frames = new struct sfm_key;
    

    oder

    frames = new struct gfm_key;
    

    Mein Problem liegt jetz darin, das ich dann nicht auf die Instanz per '->' zugreifen kann.

    Der linke Teil von '->frameType' muss auf Klasse/Struktur/Union zeigen
    

    Wie mach ich das richtig ?



  • So wie du das machen willst wird es nicht funktionieren. Man kann einem void-Pointer zwar Adressen zuweisen, aber das wars dann auch schon.

    Polymorphismus funktioniert in c++ nur mit Hilfe einer Klassenhierarchie. Wenn deine beiden Klassen sfm_key und gfm_key eine gemeinsame Basisklasse hätten, könntest du dein Vorhaben mit Hilfe eines Basisklassenzeigers realisieren.

    Der Ansatz mit void ist Murks.



  • Was gibt es für alternative Lösungen? Abgesehen von polymorphismus.



  • Pyro Phoenix schrieb:

    Was gibt es für alternative Lösungen? Abgesehen von polymorphismus.

    Ein Flag z.B., welches dir sagt, welche Struktur hinter dem Zeiger steht.



  • Pyro Phoenix schrieb:

    ...Abgesehen von polymorphismus.

    Da DAS die Standardlösung (und damit sicher, effizient, allgemein verständlich, wartbar, ....) ist, solltest Du nur mit wirklich gutem Grund darauf verzichten.
    und so einen hast Du bislang noch nicht genannt.

    Selbst, wenn Deine Klassen keine gemeinsame Basisklasse besitzen, kannst Du immer noch polymorphe Wrapper drüberlegen. Das geht IMMER, wenn sie irgendetwas gemeinsam haben ... und wenn das nicht zutrifft, dann wirst Du auch mir einem void-Pointer nicht weiterkommen.
    Badestrands Ansatz wird Dir vielleicht erstmal "leichter" erscheinen (weil Du da keine neue Technik lernen musst) - bringt aber mehr Probleme mit sich als sie löst.

    Gruß,

    Simon2.

    Gruß,

    Simon2.



  • Badestrand schrieb:

    Pyro Phoenix schrieb:

    Was gibt es für alternative Lösungen? Abgesehen von polymorphismus.

    Ein Flag z.B., welches dir sagt, welche Struktur hinter dem Zeiger steht.

    Mit anderen Worten: manuelle Polymorphie. Lass den Quatsch. Vererbung und virtuelle Funktionen sind die richtige Lösung für Dich.



  • Konrad Rudolph schrieb:

    Badestrand schrieb:

    Ein Flag z.B., welches dir sagt, welche Struktur hinter dem Zeiger steht.

    Mit anderen Worten: manuelle Polymorphie. Lass den Quatsch. Vererbung und virtuelle Funktionen sind die richtige Lösung für Dich.

    Für Polymorphie müssen aber bestimmte Voraussetzungen da sein, z.B. dass alle nötigen Operationen oder Status-Abfragen auf beiden Klassen funktionieren. Da wir aber rein gar nichts darüber wissen, können wir nicht davon ausgehen, vielleicht haben die beiden auch nichts gemeinsam. Dann läuft das wieder in Pseudo-Polymorphie mit dynamic_cast hinaus und da ist der Flag-Ansatz immerhin "ehrlicher".



  • nach dem wenigen code des OPs ließe sich auch eine lösung dieser art denken:

    #include <boost/variant.hpp>
    
    struct frame
    {
       frame* next_frame;
       frame* prev_frame;
    
       boost::variant<gfm_key, sfm_key> key;
    };
    
    //...
    frame one = { 0, 0, gfm_key("foo") };
    frame two = { 0, 0, sfm_key("foo") };
    
    get<gfm_key>(one.key);
    get<sfm_key>(two.key);
    


  • Badestrand schrieb:

    Für Polymorphie müssen aber bestimmte Voraussetzungen da sein, z.B. dass alle nötigen Operationen oder Status-Abfragen auf beiden Klassen funktionieren. Da wir aber rein gar nichts darüber wissen, können wir nicht davon ausgehen, vielleicht haben die beiden auch nichts gemeinsam.

    Hmm, na ja, ich lese aus dem Code des OP den Versuch heraus, implizites Late Binding à la VB6 zu betreiben. Das setzt dann eben doch voraus, dass die Schnittstelle in beiden Fällen gleich ist.



  • Badestrand schrieb:

    Für Polymorphie müssen aber bestimmte Voraussetzungen da sein, z.B. dass alle nötigen Operationen oder Status-Abfragen auf beiden Klassen funktionieren. Da wir aber rein gar nichts darüber wissen, können wir nicht davon ausgehen, vielleicht haben die beiden auch nichts gemeinsam. Dann läuft das wieder in Pseudo-Polymorphie mit dynamic_cast hinaus und da ist der Flag-Ansatz immerhin "ehrlicher".

    Wir wissen, dass beide Klassen zumindest gleichnamige Member haben, die sich ähnlich verhalten (sonst würde der void-Ansatz auch nicht klappen), was ja schon für Polymorphie reicht. Ich weiß nicht, warum du dynamic_cast ins Spiel bringst.



  • Michael E. schrieb:

    Wir wissen, dass beide Klassen zumindest gleichnamige Member haben, die sich ähnlich verhalten (sonst würde der void-Ansatz auch nicht klappen), was ja schon für Polymorphie reicht.

    Nicht zwingend, das kommt drauf an, vielleicht gibt es noch eine Menge mehr Zugriffe als auf einen eventuell gleichen "frameType"-Member. Da kann man doch nicht sofort wie selbstverständlich zu Polymorphie raten, wir haben doch wirklich praktisch gar keine Informationen über die Objekt-Typen und deren Verwendung.

    Michael E. schrieb:

    Ich weiß nicht, warum du dynamic_cast ins Spiel bringst.

    Ich hab's schon oft genug gesehen:

    struct A
    {
        int value;
        void* next;
    };
    
    struct B
    {
        str value;
        void* next;
    };
    
    void* list;
    ...
    if ( dynamic_cast<A*>(list) != NULL )
        dynamic_cast<A*>(list)->value = 10;
    if ( dynamic_cast<B*>(list) != NULL )
        dynamic_cast<B*>(list)->value = "12";
    

    Konrad Rudolph schrieb:

    Hmm, na ja, ich lese aus dem Code des OP den Versuch heraus, implizites Late Binding à la VB6 zu betreiben. Das setzt dann eben doch voraus, dass die Schnittstelle in beiden Fällen gleich ist.

    Es kann ja auch gut sein, dass die beiden Strukturen eine gemeinsame Basis haben und sich das Ganze perfekt mit Polymorphie bewerkstelligen lässt, aber um das zu beurteilen fehlen einfach Infos.

    Ich sage ja nicht, dass etwas gegen Polymorphie spricht, nur bisher fehlt noch was für Polymorphie, finde ich jedenfalls.



  • Badestrand schrieb:

    ...Nicht zwingend, das kommt drauf an, vielleicht gibt es noch eine Menge mehr Zugriffe als auf einen eventuell gleichen "frameType"-Member. Da kann man doch nicht sofort wie selbstverständlich zu Polymorphie raten, wir haben doch wirklich praktisch gar keine Informationen über die Objekt-Typen und deren Verwendung....

    Davon hängt das aber hier aber gar nicht ab. Für das, was mit void-Pointern und Typ-Flags hier versucht wird (von Dir oder dem OP), gibt Dir der Sprachstandard vorgefertigte Lösungen: Overloading oder Polymorphie.
    Es ist doch immer dasselbe: Ich bekomme ein "Ding", von dem ich nicht weiß, was es ist und anhand von "Typ-Informationen" bestimme ich, was ich damit tun will.... anderes machen overloads und vtables auch nicht.

    Vergleichen wir mal Code:

    struct A {   int a; };
    struct B {   void f();}
    

    "Deine" Variante (

    void process(void* data, int type) {
       if(type) {
          A* a = static_cast<A*>(data);
          a->a = 1;
       }
       else {
          B* b = static_cast<B*>(data);
          b->f();
       }
    }
    

    "Meine" Variante (overload, Compilezeitentscheidung)

    void process(A* a) {   a->a = 1; }
    void process(B* b) {   b->f(); }
    

    "Meine" Variante (polymorph, Laufzeitentscheidung)

    struct processable {
       virtual void operator()() = 0;
    };
    
    struct aProcessor : processable {
       A* a;
       aProcessor(A* ia) : a(ia) {}
       virtual void operator()() { a->a = 1; }
    };
    
    struct bProcessor : processable {
       B* b;
       bProcessor(B* ib) : b(ib) {}
       virtual void operator()() { b->f(); }
    }
    

    Mein Problem ist, dass nicht Wenige, die an "Deiner" Variante basteln, die beiden anderen Techniken oftmals gar nicht kennen ... und dann bass erstaunt (wenn nicht sogar glücklich) sind, wenn man sie ihnen zeigt. (gibt natürlich auch die "hab ich immer schon so gemacht"-Leute, die "besser" ablehnen, weil das "anders" bedeutet, aber man kann eben nicht jedem helfen ;))

    Gruß,

    Simon2.



  • Ich glaube ohne eine Rückmeldung vom OP macht die Diskussion wenig Sinn.

    Einen Grund den ich mir Vorstellen könnte der gegen Polymorphie sprechen könnte wäre die Festlegung auf POD-Strukturen die von/nach einer C-Schnittstelle kommen. Aber selbst das liese sich mit einer minimalen Wrapperklasse (die polymorph ist) lösen.

    cu André



  • Also um noch ein wenig klarheit zu schaffen:

    struct sfm_key
    {
    	int32_t			frameType;
    	int32_t			frameOriginX;
    	int32_t			frameOriginY;
    	int32_t			frameWidth;
    	int32_t			frameHeight;
    	uint8_t			*data;
    	void			*prev_frame; // sfm oder gfm key
    	void			*next_frame; // sfm oder gfm key
    };
    
    struct gfm_key
    {
    	int32_t			frameType;
    	int32_t			numIntervall;
    	float			*intervall;
    	int32_t			frameOriginX;
    	int32_t			frameOriginY;
    	int32_t			frameWidth;
    	int32_t			frameHeight;
    	uint8_t			*data;
    	void			*prev_frame; // sfm oder gfm key
    	void			*next_frame; // sfm oder gfm key
    };
    

    Mit 'new' wird dann je nach Bedarf eine der beiden Strukturen als 'root' erstellt.
    Und dann gehts je nach Fall weiter; Nach dem Double Linked-List Prinzip.

    Mit polymorphie hab ich mich noch nicht beschäftigt. So weit war ich noch nicht 😃



  • @Badestrand:

    Pyro Phoenix schrieb:

    Was gibt es für alternative Lösungen? Abgesehen von polymorphismus.

    Pyro Phoenix schrieb:

    ...
    Mit polymorphie hab ich mich noch nicht beschäftigt. So weit war ich noch nicht 😃

    Siehtste ? :p 😉 😃

    @Pyro: Sind diese structs von Dir entworfen oder vorgegeben ?
    Wenn Ersteres, würde ich da noch mehr "Typsicherheit reindesignen" und z.B. die "Listeneigenschaft" (prevFrame, nextFrame) auslagern.
    Ein

    struct sfm_key
    {
        int32_t            frameType;
        int32_t            frameOriginX;
        int32_t            frameOriginY;
        int32_t            frameWidth;
        int32_t            frameHeight;
        uint8_t            *data;
    };
    
    typedef std::list<sfm_key> sfm_key_list;
    

    sollte es besser tun.
    Außerdem scheint sich hier tatsächlich Polymorphie anzubieten.

    Und die externe Referenz auf "data" könnte man bestimmt auch besser konkretisieren (es sei denn, es handelt sich tatsächlich um einen Verweis auf ein externes uint8_t Objekt).
    ...

    Naja, nur so ein paar Ideen von mir.

    Und selbst, wenn Du die structs nicht verändern kannst, kannst Du Deinen Zugriff auf sie trotzdem mit Overloading oder Polymorphie(wrapper) machen...

    Gruß,

    Simon2.

    P.S.: Bist Du sicher, dass Du in "C++" programmierst und nicht in "C" ?



  • Simon2 schrieb:

    @Badestrand: Siehtste ? :p 😉 😃

    Ach Mist, wäre jetzt natürlich schön für mich gewesen, wenn's zwei absolut artfremde Typen wären 🤡



  • Simon2 schrieb:

    @Pyro: Sind diese structs von Dir entworfen oder vorgegeben ?

    Eigentlich trifft beides zu. Ich habe eine Datei mit einem bestimmten Format. Aber wie ich es lade bleibt mir überlassen. Ich könnte auch einfach den sfm_key weglassen und im falle eines sfm frames die Zwei zusätzlichen Variablen NULLen, aber das find ich irgendwie suboptimal. Was ich auch nicht weiß, weil es nirgends Dokumentiert ist, ob auch wirklich beide Frametypen in einer Datei vorkommen können / sollen / dürfen.

    Simon2 schrieb:

    Wenn Ersteres, würde ich da noch mehr "Typsicherheit reindesignen" und z.B. die "Listeneigenschaft" (prevFrame, nextFrame) auslagern.
    Ein

    struct sfm_key
    {
        int32_t            frameType;
        int32_t            frameOriginX;
        int32_t            frameOriginY;
        int32_t            frameWidth;
        int32_t            frameHeight;
        uint8_t            *data;
    };
    
    typedef std::list<sfm_key> sfm_key_list;
    

    sollte es besser tun.
    Außerdem scheint sich hier tatsächlich Polymorphie anzubieten.

    Hmm ... eine STL List ... neee dann müsste ich den ganzen Code re-designen.

    Simon2 schrieb:

    Und die externe Referenz auf "data" könnte man bestimmt auch besser konkretisieren (es sei denn, es handelt sich tatsächlich um einen Verweis auf ein externes uint8_t Objekt).

    Extern ? Nein. Ist ein ganz Normales char Array ( String ) in dem Später die Daten gelagert werden.

    Simon2 schrieb:

    P.S.: Bist Du sicher, dass Du in "C++" programmierst und nicht in "C" ?

    Naja ich Arbeite eigentlich in C und erweitere den Code durch C++ Funktionen, um ihn übersichtlicher zu gestalten. Ich liebe Low-Level 😃 .
    ( Hat bissher immer problemloß geklappt 🕶 )



  • Pyro Phoenix schrieb:

    ( Hat bissher immer problemloß geklappt 🕶 )

    Das sind die berühmten letzten Worte.



  • Naja ich Arbeite eigentlich in C und erweitere den Code durch C++ Funktionen, um ihn übersichtlicher zu gestalten. Ich liebe Low-Level 😃 .

    Mit C++ kann man genau so low-level arbeiten, wie mit C. (Habe mal gehört, dass in bestimmten fällen das erzeugte Programm aus C++ schneller war, als wenn man es direkt in Assembler geschrieben würde..:))



  • Konrad Rudolph schrieb:

    Pyro Phoenix schrieb:

    ( Hat bissher immer problemloß geklappt 🕶 )

    Das sind die berühmten letzten Worte.

    Is mir schon klar, das das böhse enden kann. 😃


Anmelden zum Antworten