2010-01-23 12 views
13

Esiste un modello disponibile in boost per RAII. Esistono classi come scoped_ptr, shared_ptr che funzionano fondamentalmente sul puntatore. Queste classi possono essere utilizzate per qualsiasi altra risorsa oltre ai puntatori. Esiste un modello che funzioni con risorse generali.Qualsiasi modello RAII in boost o C++ 0x

Prendiamo ad esempio una risorsa che viene acquisita all'inizio di un ambito e che deve essere in qualche modo rilasciata alla fine dello scope. Entrambi acquisiscono e rilasciano alcuni passi. Potremmo scrivere un modello che richiede due (o forse un oggetto) funtori che fanno questo compito. I havent pensato che attraverso il modo questo può essere realizzato, mi stavo chiedendo ci sono dei metodi esistenti per farlo

Edit: Come circa uno in C++ 0x con il supporto per le funzioni lambda

+1

E 'lambda, non lambada :) fisso per voi. ;) – jalf

+2

Non dovrebbe essere chiamato C++ 1x ora? – LiraNuna

+3

@LiraNuna: Non proprio, perché è un nome segnaposto. Il suo unico scopo è quello di essere conveniente e assicurarsi che tutti sappiano cosa si intende. C++ 1x è ambiguo perché 1) non siamo abituati al nome e 2) più di una revisione dello standard potrebbe verificarsi durante questo decennio. C++ 0x è quello a cui tutti sono abituati, e non c'è nient'altro che possa significare. – jalf

risposta

12

shared_ptr offre la possibilità di specificare un custom deleter. Quando il puntatore deve essere distrutto, il deleter verrà richiamato e potrà eseguire tutte le azioni di pulizia necessarie. In questo modo è possibile gestire risorse più complicate rispetto a semplici puntatori con questa classe di puntatore intelligente.

+3

così fa std :: unique_ptr. – sellibitze

+1

Le deletazioni personalizzate sono ottime, ma cosa succede se il tipo che si desidera gestire non è un puntatore? Questo accade molto quando si avvolgono librerie C che sono usate come 'esempio_t dati; example_new (&data);/* modifica i dati ... */example_free (& data) 'e se questi oggetti sono memorizzati in un contenitore persistente, è necessario memorizzare anche il puntatore gestito, il che sarebbe poco pratico e inutile. – Ponkadoodle

7

L'approccio più generica è il ScopeGuard (idea di base in this ddj article, implementata ad esempio con i macro di convenienza in Boost.ScopeExit) e consente di eseguire funzioni o ripulire le risorse all'uscita dell'ambito.

Ma a dire il vero, non vedo perché lo vorrai. Mentre capisco che è un po 'fastidioso scrivere una classe ogni volta per un modello in un passo-aquire e in un passo-passo, stai parlando di aquila-scatto a più gradini.
Se preso più passaggi, a mio parere, appartiene a una classe di utilità denominata appropriatamente, in modo che i dettagli siano nascosti e il codice in atto (riducendo così la probabilità di errore).
Se si appesantisce rispetto ai guadagni, quelle poche righe aggiuntive non sono davvero qualcosa di cui preoccuparsi.

+1

la vita sarebbe molto più facile se Stroustrop permettesse finalmente in C++ (si rifiuta categoricamente di permetterlo, sente che dovresti avere una classe ogni volta) – pm100

+3

Per essere onesti, raramente sento il bisogno di ciò che l'OP menziona perché ogni ripetizione il modello di utilizzo delle risorse * dovrebbe * essere in una classe di utilità. Aggiunto bonus: non è necessario ricordare i dettagli di gestione delle risorse. –

+15

@ pm100: No, hai sottosopra. Infine vorrebbe dire che dovrei scrivere il codice di pulizia ogni volta che uso la classe. RAII, basandosi sul distruttore, significa che posso scrivere il codice cleanup * una volta * per classe. Come è finalmente una buona idea? – jalf

4

Devo ammettere che non capisco il punto. Scrivere un wrapper RAII da zero è già ridicolmente semplice. C'è solo non molto lavoro per essere salvati utilizzando una sorta di involucro predefinita:

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

    foo& get_foo() { return f; } 

private: 
    foo f; 
}; 

Ora, le ... 's sono essenzialmente i bit che avrebbe dovuto essere compilato manualmente se è stato utilizzato un qualche tipo di RAII generale modello: creazione e distruzione della nostra risorsa foo. E senza di loro non è rimasto molto. Alcune righe di codice boilerplate, ma è così piccolo che non sembra valsa la pena estrarlo in un template riutilizzabile, almeno non al momento. Con l'aggiunta di lambda in C++ 0x, potremmo scrivere i funtori per la creazione e la distruzione in modo così conciso che è possibile che valga la pena di scriverli e inserirli in un modello riutilizzabile. Ma fino ad allora, sembra che sarebbe più un problema che altro. Se dovessi definire due funtori da inserire in un modello RAII, avresti già scritto la maggior parte di questo codice boiler due volte.

+0

lambdas Sì, è quello a cui stavo pensando, mi dispiace di non averlo menzionato nella mia domanda (era comunque nella mia mente) –

+0

Per quanto ne so, C++ 0x non definisce un simile modello. Forse perché è così semplice non c'è molto da fare, forse stanno aspettando che Boost o altre terze parti ne definiscano uno, e lascia che si provi nel mondo reale prima di standardizzarlo. Al momento, non sappiamo ancora se 1) vale la pena sforzo e 2) se ci sono trucchi non ovvi che il modello dovrebbe essere in grado di gestire. – jalf

4

stavo pensando a qualcosa di simile:

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); 
     } 
}; 

e da utilizzare in questo modo (usando GLUquadric di OpenGL come esempio):

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

un più generico e più efficiente (nessuna chiamata tramite puntatore versione di funzione) è la seguente: uso

#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_; 
}; 

Esempio:

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

Ho messo questa soluzione attraverso una compilazione reale e mi piace. Ho dovuto apportare alcune modifiche minori ma niente di orribile. –

2

Ecco ancora un altro C++ 11 RAII aiutante: https://github.com/ArtemGr/libglim/blob/master/raii.hpp

Si corre un funtore C++ alla distruzione:

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

Un'implementazione molto pericolosa: richiede l'elisione di copia/spostamento. Se non si verifica, il distruttore dell'oggetto RAIIFun viene eseguito più di una volta! – j6t

+0

@ j6t Usa Perfect Forwarding e sicuramente * non * richiede un elision per funzionare. Se stai vedendo alcuni casi d'angolo in cui viene eseguito il functor due volte, ti preghiamo di fornire MCVE. – ArtemGr

+0

È difficile fornire un MCVE come copia elision non è una garanzia, ma un'opzione per il compilatore. Il problema si verifica quando la funzione 'raiiFun' restituisce un oggetto di tipo' RAIIFun'. Se la copia/mossa non viene eliminata, il distruttore viene chiamato prima del completamento di "return". C'è un'altra copia/mossa che può essere o non essere elidata quando la variabile locale è inizializzata dal risultato della funzione: se il temporaneo non viene eletto, viene chiamato il suo distruttore. In C++ 17 viene garantita l'elisione della copia e il problema scompare. – j6t