Frage Cross-Platfrom design?



  • @SoIntMan sagte in Frage Cross-Platfrom design?:

    #if defined(OS_A)
    #include <osa.h>
    typedef OsaType DataType;
    #elif defined(OS_B)
    #include <osb.h>
    typedef OsbType DataType;
    #endif

    Wenn man es ganz schön machen wollte, könnte man hier auch was ähnliches wie HANDLE, HWND oder FILE einführen. Dadurch wäre der Header unabhängig von osa.h und osb.h.


  • Mod

    @Quiche-Lorraine sagte in Frage Cross-Platfrom design?:

    Wenn man es ganz schön machen wollte, könnte man hier auch was ähnliches wie HANDLE, HWND oder FILE einführen. Dadurch wäre der Header unabhängig von osa.h und osb.h.

    Stimmt. Für @SoIntMan : Das nennt sich PIMPL bzw. Opaque Pointer und das macht das nochmals viel besser (Auch allgemein, nicht nur für plattformabhängigen Code). https://en.wikipedia.org/wiki/Opaque_pointer



  • @SeppJ sagte in Frage Cross-Platfrom design?:

    Stimmt. Für @SoIntMan : Das nennt sich PIMPL bzw. Opaque Pointer und das macht das nochmals viel besser (Auch allgemein, nicht nur für plattformabhängigen Code). https://en.wikipedia.org/wiki/Opaque_pointer

    ja kenn ich benutzt ich an andere stellen;) in diesem platform context hab ich das mal außen or gelassen..

    danke



  • @SoIntMan sagte in Frage Cross-Platfrom design?:

    @SeppJ sagte in Frage Cross-Platfrom design?:

    Stimmt. Für @SoIntMan : Das nennt sich PIMPL bzw. Opaque Pointer und das macht das nochmals viel besser (Auch allgemein, nicht nur für plattformabhängigen Code). https://en.wikipedia.org/wiki/Opaque_pointer

    ja kenn ich benutzt ich an andere stellen;) in diesem platform context hab ich das mal außen or gelassen..

    PIMPL würd ich auf jeden Fall empfehlen, falls z.B. lediglich für einen Datentyp irgendwo <windows.h> und Konsorten in einem Header mit inkludiert werden müssten. Sonst holt man sich ganz schnell die Krätze ins Haus. In dem Kontext würde ich dann auch den anderen Namen für PIMPL verwenden: compilation firewall 😁

    (Sorry, das musste sein. Die oben erwähnten HANDLE, HWND und FILE haben mich getriggert 🙄)



  • @Finnegan sagte in Frage Cross-Platfrom design?:

    PIMPL würd ich auf jeden Fall empfehlen, falls z.B. lediglich für einen Datentyp irgendwo <windows.h> und Konsorten in einem Header mit inkludiert werden müssten. Sonst holt man sich ganz schnell die Krätze ins Haus. In dem Kontext würde ich dann auch den anderen Namen für PIMPL verwenden: compilation firewall
    (Sorry, das musste sein. Die oben erwähnten HANDLE, HWND und FILE haben mich getriggert )

    gibt es auch ein art Opaque typedef, analog zu struct?;)



  • @SoIntMan sagte in Frage Cross-Platfrom design?:

    gibt es auch ein art Opaque typedef, analog zu struct?;)

    Ich verstehe die Frage nicht ganz.

    Ziel ist es doch Implementierungsdetails zu verbergen. Und solange du dies beherzigst, kannst du Handles frei nach Herzenslust definieren.

    Das kann ein Pointer sein, ein Index, ein Bitfeld, ein Typedef, ein Struct,... sein.

    Hauptsache die Implementierungsdetails sind nach außen nicht sichtbar.



  • @SoIntMan sagte in Frage Cross-Platfrom design?:

    gibt es auch ein art Opaque typedef, analog zu struct?;)

    Nein, aber z.B. bei Windows-Typen mache ich das dann meistens so oder ähnlich:

    // public header
    struct os_data_t;
    
    class Window
    {
        ...
        private:
            os_data_t* os_data;
    };
    
    // os_a.c
    #include <windows.h>
    
    struct os_data_t
    {
        HWND hwnd;
    };
    
    ...
    

    Wichtig ist halt, im Header das #include <windows.h> zu vermeiden, damit man sich nicht so übles Zeug wie z.B. #define min ... in seinen gesamten Code reinzieht und dann bei jedem std::min(a, b) einen Heidenspaß bekommt 😉 ... es gibt zwar #define NOMINMAX für speziell dieses Problem, aber die Header haben auch noch ne Menge anderer Makros, die für Überraschungen sorgen können (z.B. near / far und solche Dinge).



  • @Finnegan sagte in Frage Cross-Platfrom design?:

    Wichtig ist halt, im Header das #include <windows.h> zu vermeiden, damit man sich nicht so übles Zeug wie z.B. #define min ... in seinen gesamten Code reinzieht und dann bei jedem std::min(a, b) einen Heidenspaß bekommt

    Spaßig ist auch winsock2.h. 😉



  • @Quiche-Lorraine sagte in Frage Cross-Platfrom design?:

    Spaßig ist auch winsock2.h. 😉

    Nicht viel mit der gemacht, aber beim groben Überfliegen fallen mir schon ein paar ungünstige Makros auf: SOCKET_ERROR, NO_ADDRESS, NO_DATA, HOST_NOT_FOUND, BIGENDIAN, LITTLEENDIAN (Kollision mit eigenen enums),. Überhaupt, viel zu viele Makros. Ich denke du meinst sowas, oder?



  • @Finnegan

    Probiere doch mal folgendes aus:

    #include <Windows.h>
    #include <winsock2.h>
    

    Unter meinem VS 2019 kommt da eine Reihe von Fehler (C2143, C2059, C4430,...) und Warnungen über Makro Neudefinitionen. Dreht man die include Reihenfolge um, so funktioniert alles.



  • @Quiche-Lorraine sagte in Frage Cross-Platfrom design?:

    Ich verstehe die Frage nicht ganz.
    Ziel ist es doch Implementierungsdetails zu verbergen. Und solange du dies beherzigst, kannst du Handles frei nach Herzenslust definieren.
    Das kann ein Pointer sein, ein Index, ein Bitfeld, ein Typedef, ein Struct,... sein.
    Hauptsache die Implementierungsdetails sind nach außen nicht sichtbar.

    ich meins ein art konzept dass ich ein primitver Typ bekannt mache, aber ihn erst durch eine Deklaration definiert. wie eben ne struktur:

    so irgendwie:

    //Header.h

    typedef struct X X;
    typedef MyInt32 <placeholder>
    

    Module.c

    struct X {...}
    <placeholder> os_32int
    
    


  • @SoIntMan sagte in Frage Cross-Platfrom design?:

    so irgendwie:

    //Header.h

    typedef struct X X;
    typedef MyInt32 <placeholder>
    

    Module.c

    struct X {...}
    <placeholder> os_32int
    
    

    Das funktioniert in C nicht. Du kannst nur Zeiger auf den struct X definieren, und damit wird die eigentliche Implementation nicht verborgen. In anderen Programmiersprachen geht das.

    Mit dem Pimpl-Idiom hast Du ein äußeren struct und Funktionen, der die API definiert und intern nutzt dieser struct einen Zeiger auf die eigentliche Datenstruktur. Dadurch wird in C die Linkabhängigkeit durchbrochen. Sprich man kann die Implementation z.B. in einer Library ändern, ohne dass man die Programme neu übersetzen muss. Es muss nur die Library neu gebaut werden.

    // API.h
    struct X {
        void* impl_;
    }
    
    void X_init(struct X *self);
    int X_foo(struct X *self);
    
    // API.c
    #include "X_impl.h"
    
    void
    X_init (struct X *self) {
         impl_ = malloc(sizeof(struct X_impl));
    }
    
    int
    X_foo(struct X *self) {
        struct X_impl *p = (struct X_impl*)impl_;
    
        return X_impl_foo(p);
    }
    


  • @SoIntMan

    Mache dir bitte etwas bewusst, du bist hier im Bereich der Schnittstellen-Definition. Also etwas, wo es manchmal heißt: Einmal Schnittstelle definieren, niemals mehr verändern, nur noch anfügen.

    Und generell dienen dir alle Definitionen der Schnittstelle nur als Abstraktionsebene zu der Implementierung.

    Dein MyInt32 ist also erstmal nicht ein 32 Bit Integer von Betriebssystem XYZ, sondern ein 32 Bit Integer auf allen Betriebssystemen. MyInt32 ist also unabhängig von irgentwelchen Betriebssystemen.

    BTW: Das Beispiel ist nicht gut, da wir hier von einem primitiven Datentyp bzw. int32_t reden.



  • @Quiche-Lorraine sagte in Frage Cross-Platfrom design?:

    BTW: Das Beispiel ist nicht gut, da wir hier von einem primitiven Datentyp bzw. int32_t reden.

    ja das habe ich auch erkannt , war bissel zu arg im over-engineering...:)
    ich werde jetzt die Standard typen c11 also in dem int32_t verwenden;)


Anmelden zum Antworten