2009-09-22 9 views
7

Angenommen, ich habe einen RAH-Stil C++ Klasse:Kann verhindert werden, dass eine Klasse im RAII-Stil "anonym" instanziiert wird?

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

... wie so in meinem Code verwendet werden:

void Manipulate() 
{ 
    StateSaver save(1); 

    // ...do stuff that modifies state 
} 

... das Ziel einige Zustand einzutreten ist, tun Zeug, dann verlasse diesen Zustand, wenn ich diesen Bereich verlasse. Gibt es eine Möglichkeit, diesen Tippfehler nicht zu kompilieren (oder zu warnen, oder sich irgendwie zu beschweren, damit der Fehler bemerkt werden kann)?

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

    // ...do stuff that modifies state 
} 

Ich bin mir nicht bewusst etwas in C++ selbst, das ich dies zu verhindern verwenden könnte, aber das bedeutet nicht, es existiert nicht. Wenn in C++ nichts vorhanden ist, sind Compiler-spezifische Erweiterungen akzeptabel. Ich interessiere mich hauptsächlich für alles, was auf GCC und MSVC abzielt (ein Icc-Tag, Ideen für andere Compiler sind willkommen, aber weniger wahrscheinlich, dass sie nützlich sind), also wären Hacks für alle nützlich (abstrahiert in geeignete # ifdef-Makrodefinitionen). .

+0

SaveMatrix(): -> Sieht für mich wie ein Funktionsaufruf. –

+0

SaveMatrix save(): -> Sieht für mich wie eine Funktionsdeklaration aus. –

Antwort

3

ich eigentlich hatte my solution in einer Reihe von Möglichkeiten von der Variante Waldo gebucht zwicken, aber was ist, ich habe schließlich eine Makro-ized Version:

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(); definiert auch kein Objekt. Es erklärt eine Funktion.

Es gibt sehr wenig, was Sie tun können, um andere (oder sich selbst, FTM) daran zu hindern, etwas anderes zu tun, als sie wollten. Das einzige, woran ich denken kann, ist nicht den Code selbst zu schreiben, sondern stattdessen ein Makro zu schreiben.

#define SAVE_MATRIX SaveMatrix save ## __LINE__ 

Dies ist jedoch ziemlich hässlich. OTOH, es fängt den Fehler zur Kompilierzeit auf.

9

Ich bin nicht sicher, ob etwas zur Kompilierzeit getan werden kann. Für eine Laufzeit zu überprüfen, können Sie dies tun:

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

, die der Kunde zu schreiben erfordert: keine Möglichkeit

SaveMatrix sm(sm); 

und es ist das gleiche für eine vorübergehende zu tun, ohne es auf eine Kennung Bindung (An diesem Punkt unterscheidet es sich nicht von einer automatischen Variable).

+2

Schön, sieht am Anfang ein bisschen komisch aus, könnte aber Teil des RAII-Idioms werden, weil die Frage ein echtes Problem mit C++/RAII aufzeigt. Ich hätte lieber ein bisschen Angst davor, herauszufinden, dass mein Schloss aus diesem Grund nicht funktioniert hat. – stefaanv

+0

Hm, von den Möglichkeiten finde ich das am besten.Es ist nicht ganz natürlich, aber RAII selbst ist auch nicht ganz natürlich, zumindest nicht, wenn man Klassen primär als Struktur-mit-Initialisierung-und-Bereinigung betrachtet. Vielen Dank! –

1

Die Klasse kann nie feststellen, ob sie als temporäres Objekt (SaveMatrix()) oder als Variable instanziiert wurde (SaveMatrix save;). Ich denke, der beste Weg, um den Programmierer zu stoppen tun, dass ohne Stapel oder Makro-Hacks ist ein Mitglied Funktionsaufruf nach dem Bau zu erzwingen, zB:

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

    void Do() 
    { 
     valid = true; 
    } 

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

Das funktioniert dann wie folgt:

{ 
    // 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

Das 'RAII(). Do()' fehlschlägt scheint hier kein Problem zu sein. AFAIU, der OP versucht, diesen Murphy-Typen auszuschließen, nicht Machiavelli. – sbi