2009-09-22 11 views
7

Załóżmy mam RAII stylu C++ Klasa:Czy można zapobiec powstawaniu "anonimowo" klasy w stylu RAII?

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

... być używane tak jak w moim kodu:

void Manipulate() 
{ 
    StateSaver save(1); 

    // ...do stuff that modifies state 
} 

... celem jest, aby podać stan, zrobić rzeczy, a następnie opuść ten stan, kiedy opuszczę ten zakres. Czy istnieje sposób, aby ten błąd nie został skompilowany (lub ostrzec, lub jakoś narzekać, aby błąd został zauważony)?

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

    // ...do stuff that modifies state 
} 

Nie jestem świadomy czegokolwiek w C++, którego mogłem użyć, aby temu zapobiec, ale to nie znaczy, że nie istnieje. Jeśli nie ma niczego w C++, dopuszczalne są rozszerzenia specyficzne dla kompilatora. Interesuje mnie przede wszystkim cokolwiek skierowane na gcc i msvc (kiedyś icc, pomysły na inne kompilatory mile widziane, ale mniej prawdopodobne, że będą użyteczne), więc przydatne byłoby hackowanie któregokolwiek z nich (wyodrębnione odpowiednio # ifdef'd definicji makr) .

+0

SaveMatrix(): -> Wygląda na to wywołanie funkcji do mnie. –

+0

SaveMatrix save(): -> Wygląda mi na funkcję deklaracji funkcji. –

Odpowiedz

3

I rzeczywiście musiał dostosować my solution w pęczek względami od wariantu Waldo pisał, ale co ja w końcu dostałem się to makro-ized wersja:

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(); nie definiuje również obiektu. Deklaruje funkcję.

Niewiele można zrobić, aby uniemożliwić innym (lub tobie, FTM) robienie czegoś innego niż oni chcieli. Jedyne, o czym mogę pomyśleć, to nie pisanie samego kodu, ale zamiast tego pisanie makra.

Jest to jednak dość brzydkie. OTOH, przechwytuje błąd podczas kompilacji.

9

Nie jestem pewien, czy cokolwiek można zrobić podczas kompilacji. Na czeku run-time, można to zrobić:

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

który wymaga klienta, aby napisać:

SaveMatrix sm(sm); 

i nie ma sposobu, aby zrobić to samo dla tymczasowy bez wiązania go do identyfikatora (w którym to momencie nie różni się od zmiennej automatycznej).

+2

Przyjemna, na początku wygląda trochę dziwnie, ale może stać się częścią idioteki RAII, ponieważ pytanie to pokazuje prawdziwy problem z C++/RAII. Wolałbym mieć trochę więcej informacji na temat tego, że mój zamek nie działa z tego powodu. – stefaanv

+0

Hm, z opcji Myślę, że podoba mi się to najlepiej.To nie jest całkiem naturalne, ale z drugiej strony sam RAII nie jest całkowicie naturalny, przynajmniej nie, jeśli myślisz o klasach głównie jako o strukturze z inicjalizacją i oczyszczeniem. Dzięki! –

1

Klasa nie może stwierdzić, czy została utworzona jako tymczasowa (SaveMatrix()), czy jako zmienna (SaveMatrix save;). Myślę, że najlepszym sposobem, aby zatrzymać programator robi to bez stosu lub hacki makro jest wymusić wywołanie funkcji członek po budowie, np

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

    void Do() 
    { 
     valid = true; 
    } 

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

to wtedy działa następująco:

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

To, że 'RAII(). Do()' nie działa, nie wydaje się tutaj problemem. AFAIU, op stara się odciąć tego Murphy'ego, a nie Machiavellego. – sbi

Powiązane problemy