Generischer Resourceholder



  • Wie sähe denn Beispiel 2 mit dem FILE * aus?
    Smartpointer fand ich nicht zwingen, zum Beispiel läuft das erste Beispiel gut ohne Smartpointer, wenn man Daten für eine Funktion locken will, und diese dann bei Ende der Funktion automatisch unlocked werden.



  • http://www.c-plusplus.net/forum/312289
    Für mutex gibt es std::lock_guard und std::unique_lock.



  • struct Deleter {
    	void operator()( FILE* f ) { fclose(f); } 
    };
    
    std::unique_ptr< FILE, Deleter > ptr( fopen("aa",0) );
    

    Irgendwie so!



  • Es gibt auch noch den ScopeGuard (Boost und Originalartikel).

    In D gibt es dafür u.a. scope(exit) , vielleicht bekommt C++ so etwas auch einmal.



  • Decimad schrieb:

    Irgendwie so!

    Oder falls man den Typ gleich behalten und nicht jedes Mal einen neuen Funktor schreiben will:

    std::unique_ptr<FILE, std::function<void()>> ptr( std::fopen("xy", "r"), &std::fclose );
    

    Geht auch mit Lambdas. Hat aber kleinen Performancenachteil durch std::function .

    <💡>



  • Ahhh, genau, da war ja eine Typweiche für empty-Deleter-Klassen eingebaut. Gut, dass Du mich gerade wieder daran erinnerst!



  • glühbirne schrieb:

    Oder falls man den Typ gleich behalten und nicht jedes Mal einen neuen Funktor schreiben will:

    std::unique_ptr<FILE, std::function<void()>> ptr( std::fopen("xy", "r"), &std::fclose );
    

    Das wär ja nur von Vorteil, wenn man noch bevor die Datei geschlossen wird, unterschiedliche Aktionen durchführen wollen würde. Wenn man das nicht braucht, sollte man das mit std::function IMHO eher sein lassen.



  • alexandrescu schrieb:

    In D gibt es dafür u.a. scope(exit) , vielleicht bekommt C++ so etwas auch einmal.

    Lieber nicht, C++ hat dafür schon ein besseres Konzept.



  • cooky451 schrieb:

    alexandrescu schrieb:

    In D gibt es dafür u.a. scope(exit) , vielleicht bekommt C++ so etwas auch einmal.

    Lieber nicht, C++ hat dafür schon ein besseres Konzept.

    Was schlägst du für scope(success) und scope(failure) als Alternative vor?



  • Destruktoren? RAII?



  • alexandrescu schrieb:

    cooky451 schrieb:

    alexandrescu schrieb:

    In D gibt es dafür u.a. scope(exit) , vielleicht bekommt C++ so etwas auch einmal.

    Lieber nicht, C++ hat dafür schon ein besseres Konzept.

    Was schlägst du für scope(success) und scope(failure) als Alternative vor?

    Zeig doch mal ein Beispiel, wo so etwas extrem nützlich ist und du nicht weißt, wie man das in C++ ohne scope(sowieso) elegant hinschreiben kann.



  • nwp3, mit Scope Guards kannst du sowas schreiben:

    void function()
    {
        Mutex m;
        m.acquire();
        ON_SCOPE_EXIT( m.release(); )    
    }
    

    Du kannst prinzipiell beliebigen Code an ON_SCOPE_EXIT übergeben. Das lohnt sich vor allem, wenn du oft verschiedene Arten zur Freigabe hast. Um Ressourcen wie Mutex oder FILE* mehrmals wiederzuverwenden, bietet sich hingegen ein eigener Typ als RAII-Wrapper an.

    Die Implementierung verwendet Lambda Expressions und Makros (wir wollen, dass für verschiedene Zeilen verschiedene Scope-Guard-Objekte erstellt werden):

    template <typename Fn>
    struct ScopeGuard
    {
    	ScopeGuard(Fn&& deleter)
    	: deleter(std::move(deleter))
    	{
    	}
    
    	~ScopeGuard()
    	{
    		deleter();
    	}
    
    	Fn deleter;
    };
    
    template <typename Fn>
    ScopeGuard<Fn> makeScopeGuard(Fn deleter)
    {
    	return ScopeGuard<Fn>(std::move(deleter));
    }
    
    #define PP_CAT_IMPL(a, b) a ## b
    #define PP_CAT(a, b) PP_CAT_IMPL(a, b)
    #define ON_SCOPE_EXIT(...) auto PP_CAT(_scopeguard, __LINE__) = makeScopeGuard([&] () { __VA_ARGS__ });
    


  • Nichts gegen Alexandrescu, aber ScopeGuard ist doch furchtbar unelegant.



  • cooky451 schrieb:

    Nichts gegen Alexandrescu, aber ScopeGuard ist doch furchtbar unelegant.

    Meinst du meinen Code? Der ist nicht von Alexandrescu, auch wenn er die zu Grunde liegende Idee hatte.

    Was ist daran unelegant? Störst du dich an den Makros?



  • Ich störe mich an der Grundidee Besitz und Freigabe zu entkoppeln.



  • cooky451 schrieb:

    Ich störe mich an der Grundidee Besitz und Freigabe zu entkoppeln.

    Ist ja auch nicht so.
    Im Beispiel mit dem Mutex-Lock "besitzt" der Scope-Guard den Lock.
    Bzw. wenn man nen FILE* und einen fclose() Scope-Guard hat, dann besitzt natürlich auch der Scope-Guard das File.

    Wobei Dinge wie ON_SCOPE_EXIT sowieso nur Hilfestellungen für Fälle sind, in denen es sich einfach nicht auszahlt eigene Guard-Klassen bzw. RAII Klassen zu schreiben, weil man irgendwas nur an 1 oder 2 Stellen braucht.



  • Für'n lock wär's okay, aber dafür gibt's lock_guard bzw. unique_lock*. Für ein FILE* ist es einfach nur fail. Was ist wenn man das FILE* zurückgeben will? Ist einfach nicht sauber.

    *und das aus gutem Grund http://stackoverflow.com/questions/6731027/boostunique-lock-vs-boostlock-guard



  • ScopeGuard soll nicht Smart-Pointer oder RAII-Wrapper ersetzen. Ich halte es aber für fragwürdig, alles, was mit Scopes und RAII zu tun hat, kategorisch in den unique_ptr -Deleter reinzuwürgen. Wie schon von hustbaer angetönt, ist ScopeGuard im Bezug auf Freigabe vor allem nützlich, wenn sich das Schreiben einer eigenen Klasse nicht lohnt.

    Zum anderen kann man damit recht elegant Rollback-Semantik implementieren. Entsprechende Aktionen sind meist kontext-spezifisch und nicht zwingend an ein einzelnes Ressourcen-Objekt gebunden. Eine eigene Klasse müsste die Logik vom Anwendungscode trennen und hätte nur eine einzige Instanz. Mit ON_SCOPE_EXIT drückt man hingegen direkt am Anfang des Blocks aus: Falls der Commit fehlschlägt, sieht der Rollback so aus. Man hat damit ein nützliches Tool, um Exceptionsicherheit zu realisieren.



  • Nexus schrieb:

    ScopeGuard soll nicht Smart-Pointer oder RAII-Wrapper ersetzen.

    Wird aber leider oft dafür benutzt.

    Nexus schrieb:

    Ich halte es aber für fragwürdig, alles, was mit Scopes und RAII zu tun hat, kategorisch in den unique_ptr -Deleter reinzuwürgen.

    Stimmt, wir brauchen unique_handle. 👍

    Nexus schrieb:

    Wie schon von hustbaer angetönt, ist ScopeGuard im Bezug auf Freigabe vor allem nützlich, wenn sich das Schreiben einer eigenen Klasse nicht lohnt.

    Inwiefern ist unique_ptr/unique_handle nutzen äquivalent zum Schreiben eine eigenen Klasse?

    Nexus schrieb:

    Zum anderen kann man damit recht elegant Rollback-Semantik implementieren. Entsprechende Aktionen sind meist kontext-spezifisch und nicht zwingend an ein einzelnes Ressourcen-Objekt gebunden. Eine eigene Klasse müsste die Logik vom Anwendungscode trennen und hätte nur eine einzige Instanz. Mit ON_SCOPE_EXIT drückt man hingegen direkt am Anfang des Blocks aus: Falls der Commit fehlschlägt, sieht der Rollback so aus. Man hat damit ein nützliches Tool, um Exceptionsicherheit zu realisieren.

    Dafür ist es okay. Nicht für Ressourcen.



  • cooky451 schrieb:

    Nexus schrieb:

    Zum anderen kann man damit recht elegant Rollback-Semantik implementieren. Entsprechende Aktionen sind meist kontext-spezifisch und nicht zwingend an ein einzelnes Ressourcen-Objekt gebunden. Eine eigene Klasse müsste die Logik vom Anwendungscode trennen und hätte nur eine einzige Instanz. Mit ON_SCOPE_EXIT drückt man hingegen direkt am Anfang des Blocks aus: Falls der Commit fehlschlägt, sieht der Rollback so aus. Man hat damit ein nützliches Tool, um Exceptionsicherheit zu realisieren.

    Dafür ist es okay. Nicht für Ressourcen.

    Ach Cooky, lass mal die Bibel stecken.

    Es macht einfach keinen Sinn für eine Resource deren Typ man 1x oder 2x im ganzen Programm braucht eine eigene scoped_xxx Deleter-Hilfsfunktion oder Hilfsklasse zu schreiben.
    Wenn man was fertiges hat, OK, verwenden, aber wenn man sich erst noch Code schreiben sollte ... neh, in solchen Fällen nehme ich gerne ON_SCOPE_EXIT . Und nu erzähl mir bitte nicht dass das unsauber ist.

    BTW: entwickelst du irgendwelche Projekte wo es eine Rolle spielt wie lange du dafür brauchst? Beispielsweise weil dich jmd. dafür bezahlt? Es macht nämlich sehr den Eindruck dass das nicht so wäre.


Anmelden zum Antworten