[Design] Wrapper-Klasse um C Handle kopierbar?



  • Spontane Idee:

    class File : boost::noncopyable {
      FILE *handle;
      friend class FileHandle; // der Freund darf kopieren
    public:
      /* ... */
    };
    
    class FileHandle {
      File file; // kein Pointer
    public:
      File* operator->() { return &file; }
      // falls vorhanden, solche Sachen dann hier:
      size_t reference_count() const { return c_core_file_get_ref_count(file.handle); }
    };
    

    Damit wäre, mit etwas Mogelei, die Doppelfunktionalität des Handles wieder in zwei getrennte Klassen untergebracht. (Ironischerweise geht das mit C++11 nicht mehr.) Wobei, irgendwie kommt mir das komisch vor...



  • Ich würde mich an std::unique_ptr orientieren und das Handle move-only machen. Alles andre macht imo keinen Sinn, außer du willst es sharen. In dem Fall dann eben Reference-Counting. Alternativ kannst du dir auch überlegen, ob du das Handle beim Kopieren nicht vielleicht duplizieren willst.



  • pumuckl schrieb:

    Dravere schrieb:

    a) Die Klasse umbenennen? Z.B. FileHandle? Mich stört dann irgendwie, dass eine FileHandle Klasse Methoden für das Lesen und Schreiben aufweist. Bin ich da zu pingelig?

    Find ich zu pingelig, wo du schon so fragst 😉
    Ein Handle ist halt nichts als ein besserer Pointer, Proxy oder wie auch immer - warum solltest du nicht Funktionalität bieten, um den Zugiff über das handle auf die dahinterliegende Ressource zu vereinfachen?

    Ich finde aber auch, dass die Klasse, wenn sie wirklich nur ein RAII Wrapper um ein low-level Handle sein soll, nicht unbedingt über Methoden wie read() verfügen sollte. Ich würde mir überlegen, ob es nicht vielleicht sinnvoll wäre, eine benutzerdefinierte Typumwandlung nach FILE* zu bieten, sodass man das RAII Objekt völlig gleich wie einen normalen FILE* verwenden kann. Wenn es eine vordefinierte read() Methode hat, dann ist es imo schon eher eine vollwertige File-Klasse und kein einfacher Handle-Wrapper mehr. Allerdings wirft das natürlich sofort die Frage auf: Warum keine iostreams?


  • Administrator

    @dot,
    Hast du meinen ersten Beitrag gelesen?
    1. C++11 kann ich nicht verwenden, daher liegt die Move-Semantik nicht drin.
    2. FILE ist hier nur als Platzhalter zu verstehen. Tatsächlich geht es um ganz andere Objekte und Funktionalitäten (heisst auch alles ganz anders), aber die C Schnittstelle ist eben genau gleich gebaut. Habe hier nur FILE verwendet, weil damit allen klar sein sollte, wie die Schnittstelle in etwa aussieht.

    dot schrieb:

    Alternativ kannst du dir auch überlegen, ob du das Handle beim Kopieren nicht vielleicht duplizieren willst.

    Was genau verstehst du darunter? Ich kann über die C Schnittstelle aus einem bestehenden Handle nicht ein neues machen. Ich muss eine neue Abfrage absetzen, heisst dieselbe wie vorher, um ein neues Handle auf das Gleiche zu erhalten.

    @giest,
    Hmmm ... mehr fällt mir dazu aktuell auch nicht ein. Kommt mir ebenfalls seltsam vor...
    Dann irgendwie lieber meine Idee, dass man File lokal über FileHandle baut:

    class file_handle
    {
      FILE* m_raw_handle;
    
    public:
      file_handle(FILE* raw_handle)
        : m_raw_handle(raw_handle)
      {
      }
    
      friend FILE* get_raw_handle(file_handle);
    }
    
    FILE* get_raw_handle(file_handle handle)
    {
      return handle.m_raw_handle;
    }
    
    class file : boost::noncopyable
    {
      FILE* m_raw_handle;
    
    public:
      file(file_handle handle)
        : m_raw_handle(get_raw_handle(handle))
      {
      }
    
      // Methoden
    }
    
    // ...
    
    void foo(file_handle foo_handle)
    {
      file foo_file(foo_handle);
    
      // ...
    }
    

    Grüssli



  • Dravere schrieb:

    1. C++11 kann ich nicht verwenden, daher liegt die Move-Semantik nicht drin.

    Das ist mir leider entgangen.

    Dravere schrieb:

    1. FILE ist hier nur als Platzhalter zu verstehen. Tatsächlich geht es um ganz andere Objekte und Funktionalitäten (heisst auch alles ganz anders), aber die C Schnittstelle ist eben genau gleich gebaut. Habe hier nur FILE verwendet, weil damit allen klar sein sollte, wie die Schnittstelle in etwa aussieht.

    Vergisses, für einen Moment hab ich da an Posix-Handles gedacht...



  • Dravere schrieb:

    Dann irgendwie lieber meine Idee, dass man File lokal über FileHandle baut

    Finde ich verkehrt. Normalerweise wrappt man ja die ursprüngliche, unkopierbare Ressource und macht sie kopierbar ( FILE* macht das). Nur das ist logisch. Man baut aus T einen shared_ptr<T> und nicht umgekehrt, das wäre dann völlig unintuitiv in der Handhabung.

    Meins leuchtet ein: Unkopierbare File-Klasse -> Smart Pointer drum und gut. Das einzig spezielle ist, dass der erste Wrapper Funktionalität vorbehält. Genau genommen ist das jedoch ein Fehler von FILE* und nicht vom Wrapper, insofern kann man das nicht anders trennen. Und die Trennung ist ja genau das, was du willst. Wenn ein FileHandle selbst keinen Namen hat, muss es etwas drunter haben und ein File selbst kann keine Referenzzählung vornehmen. Es läuft auf diese zwei Klassen hinaus und das FileHandle muss das File wrappen.

    Je mehr ich drüber nachdenke, desto passender finde ich meinen Vorschlag. Und die seltsame Implementierung ist kein Designfehler vom Wrapper, sondern vom Gewrappten.

    Vielleicht wird der Wrap schmackhafter, wenn du FileHandle in file_ptr umbenennst.



  • Destructive Copy?

    Edit: Vergiss es, das geht ja mit vector nicht.


  • Administrator

    @giest,
    So ganz unrecht hast du nicht. Hab mir heute deine Lösung nochmals angeschaut und in den Kontext reingedacht. Es hätte ein paar bestechende Vorteile. Ich nehme die Lösung nun zumindest in die engere Auswahl mit auf. Danke.

    Ich muss mich zum Glück noch nicht heute oder morgen entscheiden und kann nun nochmals alles in Ruhe evaluieren. Danke für die Vorschläge 🙂

    Falls ihr noch weitere habt oder Kommentare loswerden wollt, dann nur zu 🙂

    Grüssli



  • Hm, noch ne Idee. Du könntest den Datenmember mutable machen und im op= moven. Wäre allerdings ziemlich unintuitiv. Noch eine Möglichkeit wäre eine Art Move-Proxy.



  • 314159265358979 schrieb:

    Hm, noch ne Idee. Du könntest den Datenmember mutable machen und im op= moven. Wäre allerdings ziemlich unintuitiv.

    Vor allem recht buganfällig, wenn sich STL-Container und -Algorithmen auf normale Kopiersemantik verlassen.



  • Achja richtig. Das war wohl der Grund, warum man auto_ptr nicht in Containern speichern kann.


Anmelden zum Antworten