2009-09-22 5 views
7

Supongamos que tengo una clase de estilo RAII C++:¿Es posible evitar que una clase de estilo RAII sea instanciada "anónimamente"?

class StateSaver 
{ 
    public: 
    StateSaver(int i) { saveState(); } 
    ~StateSaver() { restoreState(); } 
}; 

... para ser utilizado como tal en mi código:

void Manipulate() 
{ 
    StateSaver save(1); 

    // ...do stuff that modifies state 
} 

... siendo el objetivo de entrar en algún estado, hacer cosas, luego deja ese estado cuando salga de ese alcance. ¿Hay alguna forma de que este error no se compile (o advierta, o se queje de alguna manera para que se note el error)?

void Manipulate() 
{ 
    StateSaver(1); // ruh-roh, state saved and immediately restored! 

    // ...do stuff that modifies state 
} 

No estoy al tanto de cualquier cosa en C++ sí que podría utilizar para evitar esto, pero eso no quiere decir que no exista. Si no hay nada en C++, las extensiones específicas del compilador serían aceptables. Principalmente estoy interesado en cualquier cosa que tenga como objetivo gcc y msvc (algún día icc, las ideas para otros compiladores son bienvenidas pero es menos probable que sean útiles) así que los piratear para cualquiera de ellas serían útiles (abstraído en definiciones de macro # ifdef'd apropiadamente, por supuesto) .

+0

SaveMatrix(): -> Parece que una llamada de función a mí. –

+0

SaveMatrix save(): -> Parece una declaración de función para mí. –

Respuesta

3

que en realidad tuvo que ajustar my solution en un montón de formas de la variante Waldo publicado, pero lo que finalmente llegué a una versión macro-izada de:

class GuardNotifier 
{ 
    bool* notified; 
    public: 
    GuardNotifier() : notified(NULL) { } 
    void init(bool* ptr) { notified = ptr; } 
    ~GuardNotifier() { *notified = true; } 
}; 
class GuardNotifyReceiver 
{ 
    bool notified; 
    public: 
    GuardNotifyReceiver() : notified(false) { } 
    void init(const GuardNotifier& notifier) 
     { const_cast<GuardNotifier&>(notifier).init(&notified); } 
    ~GuardNotifyReceiver() { assert(notified); } 
}; 
class StateSaver 
{ 
    GuardNotifyReceiver receiver; 
    public: 
    StateSaver(int i, 
       const GuardNotifier& notifier = GuardNotifier()) 
    { 
     receiver.init(notifier) 
     saveState(); 
    } 
    ~StateSaver() 
    { 
     restoreState(); 
    } 
}; 
6

SaveMatrix save(); tampoco define un objeto. Declara una función.

Hay muy poco que puede hacer para evitar que otros (o usted mismo, FTM) hagan algo más de lo que quisieran. Lo único que se me ocurre es no escribir el código en sí, sino escribir una macro en su lugar.

#define SAVE_MATRIX SaveMatrix save ## __LINE__ 

Sin embargo, esto es bastante feo. OTOH, capta el error en tiempo de compilación.

9

No estoy seguro de si se puede hacer algo en tiempo de compilación. Para una comprobación en tiempo de ejecución, se puede hacer esto:

struct SaveMatrix 
{ 
    SaveMatrix(const SaveMatrix& that) { 
     assert(this == &that); 
     glPushMatrix(); 
    } 
    ~SaveMatrix() { glPopMatrix(); } 
}; 

que requiere el cliente para escribir:

SaveMatrix sm(sm); 

y no hay forma de hacer lo mismo para un temporal sin enlazarlo a un identificador (en ese punto no es diferente de una variable automática).

+2

Bueno, parece un poco raro al principio, pero podría formar parte del idioma RAII, porque la pregunta muestra un problema real con C++/RAII. Prefiero tener un poco de error al descubrir que mi bloqueo no funcionó debido a esto. – stefaanv

+0

Hm, de las opciones, creo que me gusta más.No es del todo natural, pero una vez más, el RAII tampoco es del todo natural, al menos no si se piensa en las clases principalmente como estructuras-con-inicialización-y-limpieza. ¡Gracias! –

1

La clase nunca puede decir si se creó una instancia como temporal (SaveMatrix()) o como una variable (SaveMatrix save;). Creo que la mejor manera de detener el programador haciendo que sin pila o hacks macro es forzar una llamada a la función miembro después de la construcción, por ejemplo:

class RAII 
{ 
public: 
    bool valid; 
    RAII() 
     : valid(false) 
    { 
     cout << "RAII ctor" << endl; 
    } 

    void Do() 
    { 
     valid = true; 
    } 

    ~RAII() 
    { 
     assert(valid); 
     cout << "RAII dtor" << endl; 
    } 
}; 

Esto entonces funciona de la siguiente manera:

{ 
    // Intended use 
    RAII raii; 
    raii.Do(); 

    cout << "Some task" << endl; 
} 

{ 
    // Woops: forgot Do() 
    RAII raii; 

    cout << "Some task" << endl; 
} 

{ 
    // Woops: forgot Do() 
    RAII(); 

    cout << "Some task" << endl; 
} 

{ 
    // Programmer shot self in foot, hopefully the act of typing this would make them realise that 
    RAII().Do(); 

    cout << "Some task" << endl; 
} 
+1

Que 'RAII(). Do()' falla no parece ser un problema aquí. AFAIU, el operador intenta excluir a ese tipo Murphy, no a Maquiavelo. – sbi

Cuestiones relacionadas