2010-01-23 5 views
13

Gibt es eine verfügbare Vorlage in Boost für RAII. Es gibt Klassen wie scoped_ptr, shared_ptr, die grundsätzlich auf Zeiger arbeiten. Können diese Klassen für andere Ressourcen als Zeiger verwendet werden? Gibt es eine Vorlage, die mit allgemeinen Ressourcen arbeitet?Beliebige RAII-Vorlage in Boost oder C++ 0x

Nehmen Sie zum Beispiel eine Ressource, die am Anfang eines Bereichs erworben wird und am Ende des Bereichs irgendwie freigegeben werden muss. Beide erwerben und veröffentlichen einige Schritte. Wir könnten eine Vorlage schreiben, die zwei (oder vielleicht ein Objekt) Funktoren benötigt, die diese Aufgabe übernehmen. Ich habe nicht gedacht, es durch, wie dies erreicht werden kann, ich habe mich nur gefragt gibt es irgendwelche bestehenden Methoden es mit Unterstützung für Lambda-Funktionen

+1

Es Lambda ist, nicht Lambada :) Fest es für Sie. ;) – jalf

+2

Soll das nicht jetzt C++ 1x heißen? – LiraNuna

+3

@LiraNuna: Nicht wirklich, weil es ein Platzhaltername ist. Sein einziger Zweck ist es, bequem zu sein und sicherzustellen, dass jeder weiß, was gemeint ist. C++ 1x ist mehrdeutig, weil 1) wir nicht an den Namen gewöhnt sind und 2) mehr als eine Revision des Standards in diesem Jahrzehnt auftreten kann. C++ 0x ist diejenige, an die jeder gewöhnt ist, und es kann nichts anderes bedeuten. – jalf

Antwort

12

shared_ptr bietet die Möglichkeit, eine custom deleter anzugeben. Wenn der Zeiger zerstört werden muss, wird der Deleter aufgerufen und kann alle erforderlichen Bereinigungsaktionen ausführen. Auf diese Weise können mit dieser Smart-Pointer-Klasse komplexere Ressourcen als einfache Pointer verwaltet werden.

+3

so std :: unique_ptr. – sellibitze

+1

Benutzerdefinierte Deletes sind großartig, aber was, wenn der Typ, den Sie verwalten möchten, kein Zeiger ist? Dies passiert beim Umbrechen von C-Bibliotheken, die wie 'example_t data verwendet werden; example_new (&data);/* manipulieren Sie die Daten ... */example_free (& data) ', und wenn diese Objekte in einem persistenten Container gespeichert sind, müssen Sie auch den verwalteten Zeiger speichern, was unpraktisch und verschwenderisch wäre. – Ponkadoodle

7

Der allgemeinste Ansatz

Edit: Wie wäre es ein in C++ 0x zu tun ist, die ScopeGuard man (Grundidee in this ddj article, implementiert z. B. mit Komfort-Makros in Boost.ScopeExit), und können Sie Funktionen ausführen oder Ressourcen beim Geltungsbereich Exit zu bereinigen.

Aber um ehrlich zu sein, sehe ich nicht, warum du das willst. Während ich verstehe, dass es ein bisschen nervig ist, jedes Mal eine Klasse für ein One-Step-Aquire- und One-Step-Release-Pattern zu schreiben, sprichst du von Multi-Step-Aquire und -Release.
Wenn es mehrere Schritte dauert, gehört es meiner Meinung nach in eine entsprechend benannte Dienstprogrammklasse, so dass die Details verborgen sind und der Code vorhanden ist (wodurch die Fehlerwahrscheinlichkeit verringert wird).
Wenn Sie es gegen die Gewinne abwägen, sind diese wenigen zusätzlichen Zeilen nicht wirklich etwas, worüber man sich Sorgen machen muss.

+1

Leben wäre so viel einfacher, wenn Stroustrop würde endlich in C++ zulassen (er lehnt es flach zu verweigern, er fühlt, dass Sie jedes Mal eine Klasse haben sollten) – pm100

+3

Um ehrlich zu sein, ich selten das Bedürfnis nach was das OP erwähnt, weil jeder wiederholt Das Ressourcennutzungsmuster * sollte * in einer Dienstprogrammklasse enthalten sein. Zusätzlicher Bonus - Sie müssen sich nicht an Details zur Ressourcenverwaltung erinnern. –

+15

@ pm100: Nein, du hast es auf den Kopf gestellt. Schließlich würde ich jedes Mal, wenn ich die Klasse benutze, Bereinigungscode schreiben müssen. RAII, basierend auf dem Destruktor, bedeutet, dass ich den Bereinigungscode * einmal * pro Klasse schreiben kann. Wie ist es endlich wieder eine gute Idee? – jalf

4

Ich muss zugeben, ich sehe nicht wirklich den Punkt. Einen RAII-Wrapper von Grund auf zu schreiben, ist bereits lächerlich einfach. Es gibt einfach nicht viel Arbeit mit einer Art von vordefinierten Wrapper gespeichert werden:

struct scoped_foo : private boost::noncopyable { 
    scoped_foo() : f(...) {} 
    ~scoped_foo() {...} 

    foo& get_foo() { return f; } 

private: 
    foo f; 
}; 

Nun werden die ... ‚s sind im Wesentlichen die Bits, die manuell werden müssten ausgefüllt, wenn Sie irgendeine Art von allgemeinen RAII verwendet Vorlage: Erstellung und Vernichtung unserer Ressource foo. Und ohne sie ist wirklich nicht viel übrig. Ein paar Zeilen Code, aber es ist so klein, dass es sich einfach nicht lohnt, es in eine wiederverwendbare Vorlage zu extrahieren, zumindest im Moment nicht. Mit dem Hinzufügen von Lambdas in C++ 0x könnten wir die Funktoren für Erzeugung und Zerstörung so prägnant schreiben, dass es sich lohnt, diese zu schreiben und sie in eine wiederverwendbare Vorlage zu stecken. Aber bis dahin scheint es mehr Mühe als wert zu sein. Wenn Sie zwei Funktoren definieren würden, die in eine RAII-Vorlage eingefügt werden sollen, hätten Sie bereits den Großteil dieses Standardcodes zweimal geschrieben.

+0

lambdas Ja, das ist, worüber ich nachgedacht habe Entschuldigung dafür, dies in meiner Frage nicht erwähnt zu haben (es war in meinen Gedanken) –

+0

Soweit ich weiß, definiert C++ 0x keine solche Vorlage, vielleicht weil es so einfach ist gibt es nicht viel Sinn, vielleicht warten sie darauf, dass Boost oder andere Dritte einen definieren, und lassen es sich in der realen Welt beweisen, bevor sie standardisiert werden.Im Moment wissen wir noch nicht, ob 1) es sich lohnt Bemühung und 2) wenn es irgendwelche nicht offensichtlichen Fehler gibt, mit denen die Vorlage umgehen könnte. – jalf

4

Ich dachte an etwas ähnliches:

template <typename T> 
class RAII { 
    private: 
     T (*constructor)(); 
     void (*destructor)(T); 
    public: 
     T value; 
     RAII(T (*constructor)(), void (*destructor)(T)) : 
        constructor(constructor), 
        destructor(destructor) { 
      value = constructor(); 
     } 
     ~RAII() { 
      destructor(value); 
     } 
}; 

und wie diese verwendet werden (OpenGL GLUquadric als Beispiel):

RAII<GLUquadric*> quad = RAII<GLUquadric*>(gluNewQuadric, gluDeleteQuadric); 
gluSphere(quad.value, 3, 20, 20) 
8

Ein allgemeineres und effizienter (kein Anruf durch Funktionszeiger) Version ist wie folgt:

#include <boost/type_traits.hpp> 

template<typename FuncType, FuncType * Func> 
class RAIIFunc 
{ 
public: 
    typedef typename boost::function_traits<FuncType>::arg1_type arg_type; 
    RAIIFunc(arg_type p) : p_(p) {} 
    ~RAIIFunc() { Func(p_); } 
    arg_type & getValue() { return p_; } 
    arg_type const & getValue() const { return p_; } 
private: 
    arg_type p_; 
}; 

Beispiel Verwendung:

RAIIFunc<int (int), ::close> f = ::open("..."); 
+1

Ich lege diese Lösung durch eine eigentliche Kompilierung und mag es. Ich musste einige kleine Änderungen vornehmen, aber nichts Schreckliches. –

2

Hier ist noch eine weitere C++ 11 RAH Helfer: https://github.com/ArtemGr/libglim/blob/master/raii.hpp

Es läuft eine C++ Funktors bei Zerstörung:

auto unmap = raiiFun ([&]() {munmap (fd, size);}); 
+0

Eine sehr gefährliche Implementierung: Es erfordert, dass Kopieren/Verschieben Elision auftritt. Wenn es nicht auftritt, wird der Destruktor des RAIIFun-Objekts mehr als einmal ausgeführt! – j6t

+0

@ j6t Es verwendet Perfect Forwarding und erfordert ganz sicher keine Unterstützung. Wenn Sie einen Eckfall sehen, in dem der Funktor zweimal ausgeführt wird, geben Sie MCVE an. – ArtemGr

+0

Es ist schwierig, einen MCVE als Kopie zur Verfügung zu stellen. Elision ist keine Garantie, sondern eine Option für den Compiler. Das Problem tritt auf, wenn die Funktion 'raiiFun' ein Objekt vom Typ' RAIIFun' zurückgibt. Wenn die Kopie/Verschiebung nicht entfernt wird, wird der Destruktor aufgerufen, bevor die Rückkehr ausgeführt wird. Es gibt eine weitere Kopie/Verschiebung, die möglicherweise nicht entfernt wird, wenn die lokale Variable aus dem Funktionsergebnis initialisiert wird: Wenn das temporäre Element nicht entfernt wird, wird sein Destruktor aufgerufen. In C++ 17 erhalten wir eine garantierte Kopie und das Problem verschwindet. – j6t