Design Problem: Zugriff auf RAII Resource



  • Wie greife ich am besten auf ein Element einer Klasse zu, welches per RAII implementiert wurde?

    // Funktionen LoadBitmap(), DeleteBitmap(), SetBitmap() sind durch die Bib (WinAPI) vorgegeben
    class Bitmap
    {
    private:
      HANDLE BitmapHandle;
    
    public:
      Bitmap() :
        BitmapHandle(LoadBitmap("Test.bmp"))
      {}     
    
      ~Bitmap()
      {
        DeleteBitmap(BitmapHandle);
      }
    
      // Lösung 1: Zugriff über Kopie
      HANDLE GetBitmap()
      {
        return this->BitmapHandle;
      }
      // Lösung 2: Implementierung der Funktion innerhalb der Klasse 
      void SetBitmap(Fenster MeinFenster)
      {
        return ::SetBitmap(MeinFenster, this->BitmapHandle);
      }
    }; 
    
    int Test()
    {  
      Bitmap B;
    
      // Lösung 1
      SetBitmap(MeinFenster, B.GetBitmap());
      // Lösung 2
      B.SetBitmap(MeinFenster);
    }
    


  • Konzept ist fehlerhaft, da das Handle eine gemeiname Ressource von dem Fenster und der Bitmapklasse ist. D.h. Ownership des Handles muss ans Fenster uebertragen werden. Und dann hast du einen unique-Pointer fuer Bitmap-Handles als Klasse.



  • Hmm...

    Ich wollte eine RAII Klasse schreiben, welche mir das Laden und insbesondere das Freigeben von Bitmaps erleichtert.

    class MeineGUI
    {
    private:
      HANDLE Bitmap1;
      // ...
      HANDLE Bitmap20;
    
      // vs
      Bitmap Bitm1;
      // ...
      Bitmap Bitm20;
    
    public:
      MeineGUI() :
        Bitm1("bbb") , 
        // ...
        Bitm20("bbb")
      {
        this->Bitmap1 = LoadBitmap("bbb");
        // ...
        this->Bitmap20 = LoadBitmap("bbb");    
      }
    
      ~MeineGUI()
      {
        DeleteBitmap(this->Bitmap1);
        // ...
        DeleteBitmap(this->Bitmap20);
      }
    };
    


  • @knivil:
    Meinst du folgendes?

    class BitmapDeleter
    {
    public:
      void operator()(HANDLE* Bitmap)
      {
        DeleteBitmap(*Bitmap);
        delete Bitmap;
      }
    };
    
    class MeineGUI
    {
    private:
      std::unique_ptr<HANDLE, BitmapDeleter> FacadeBitmap;
    
    public:
      MeinGui() :
        FacadeBitmap(new HANDLE(LoadBitmap("bbb")))
      {
      }
    };
    


  • Ich verstehe nicht, wieso das Fenster nicht einfach Ownership von der Bitmap-Klasse haben soll und die Bitmap-Klasse Ownership vom Handle. Dann hat das Fenster doch gleichzeitig Ownership vom Handle.

    HANDLE ist als PVOID definiert, also würde ich mit new/delete hier überhaupt nicht rumhantieren.



  • Ja, aber der Punkt ist, dass unique_ptr einen Pointer dranhängt an HANDLE.
    Das heißt man muss das HANDLE selber entweder via new erzeugen oder remove_ptr auf HANDLE klatschen und dann einfach nur LoadBitmap übergeben.



  • Bitte ein Bit schrieb:

    class BitmapDeleter
    {
    public:
      void operator()(HANDLE* Bitmap)
      {
        DeleteBitmap(*Bitmap);
        delete Bitmap;
      }
    };
    
    class MeineGUI
    {
    private:
      std::unique_ptr<HANDLE, BitmapDeleter> FacadeBitmap;
    
    public:
      MeinGui() :
        FacadeBitmap(new HANDLE(LoadBitmap("bbb"))) //A
      {
      }
    };
    

    Du hast in Zeile A ein Ressourcenleck. Wer gibt die Bitmap wieder frei, wenn new HANDLE fehlschlägt? Es ist nämlich nicht vorgeschrieben, dass die Speicheranforderung bei new vor der Auswertung der Konstruktorargumente stattfindet.
    Außerdem bietet es sich im Deleter an, auf nullptr zu prüfen. std::shared_ptr ruft den nämlich auch mit nullptr auf.
    Und es wäre vielleicht schön bei Fehlschlag von LoadBitmap zu werfen.

    Was ist das Problem an unique_ptr<std::remove_pointer<HANDLE>::type, BitmapDeleter> ? Der Typ von HANDLE wird sich doch niemals ändern.

    typedef std::unique_ptr<std::remove_pointer<HANDLE>::type, BitmapDeleter> bitmap_handle;
    
    bitmap_handle load_bitmap(char const *s)
    {
    	bitmap_handle result{LoadBitmap(s)};
    	if (!result)
    	{
    		throw std::runtime_error(...); //oder so
    	}
    	return result;
    }
    


  • Erstmals danke für die Info's! 👍

    Was ist das Problem an unique_ptr<std::remove_pointer<HANDLE>::type, BitmapDeleter>?

    Ich kenne mich leider nicht so gut mit der STL aus. Ich kenne die ganzen Standardcontainer. Aber Dinge wie std::remove_pointer sind noch Neuland für mich.

    Ich musste jetzt erstmals schauen wie ich aus *bitmap_handle wieder ein HANDLE bekomme. Genauer gesagt nutze ich HBITMAP.



  • Bitte ein Bit schrieb:

    Aber Dinge wie std::remove_pointer sind noch Neuland für mich.

    LOL



  • Also wenn du mich fragst ist Variante 1 in deinem Anfangsposting the way to go, wenn du nur einen RAII Wrapper für ein rohes HBITMAP haben willst (ich würd überhaupt einfach eine User Defined Conversion nach HBITMAP machen und fertig). Der Sinn von einem std::remove_pointer<HANDLE> will sich mir außerdem nicht erschließen, das sieht mir eher aus wie der Versuch, die Grundidee eines HANDLE zu untergraben...



  • dot schrieb:

    Der Sinn von einem std::remove_pointer<HANDLE> will sich mir außerdem nicht erschließen, das sieht mir eher aus wie der Versuch, die Grundidee eines HANDLE zu untergraben...

    Ne, da holt man den Pointer nur vorher ausm Typen raus, damit unique_ptr ihn später wieder dran kleben kann. Das passt schon so. unique_handle wäre natürlich schöner, aber das gibt's ja noch nicht.



  • cooky451 schrieb:

    dot schrieb:

    Der Sinn von einem std::remove_pointer<HANDLE> will sich mir außerdem nicht erschließen, das sieht mir eher aus wie der Versuch, die Grundidee eines HANDLE zu untergraben...

    Ne, da holt man den Pointer nur vorher ausm Typen raus, damit unique_ptr ihn später wieder dran kleben kann. Das passt schon so.

    Das alles funktioniert aber nur unter der Voraussetzung, dass HANDLE tatsächlich ein Pointertyp ist, was in der Praxis natürlich so ist; dennoch: imo ein extrem unsauberer Hack. unique_handle o.ä. ist viel zu schnell geschrieben, als dass derartiger Missbrauch von unique_ptr irgendwie zu rechtfertigen wäre...



  • Das hatte ich schon befürchtet. Ich kenne auch Handle Implementierungen, welche nur einen Index auf einer Tabelle darstellen.

    Also ist die erste Lösung doch nicht so schlecht.


Anmelden zum Antworten