7

RAIIスタイルのC++クラスがあるとします。

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

...私のコードでそのように使用されます:

void Manipulate()
{
  StateSaver save(1);

  // ...do stuff that modifies state
}

...目標は、ある状態に入り、何かを行い、そのスコープを離れるときにその状態を離れることです。このタイプミスをコンパイルしないようにする方法はありますか(または警告するか、間違いに気付くことができるように何らかの形で文句を言う)?

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

  // ...do stuff that modifies state
}

これを防ぐために使用できるC++自体の内容はわかりませんが、それが存在しないという意味ではありません。C ++に何もない場合は、コンパイラ固有の拡張機能を使用できます。私は主にgccとmsvcをターゲットにするものに興味があります(いつかicc、他のコンパイラのアイデアは歓迎されますが、役に立つ可能性は低いです)ので、それらのいずれかをハックすると便利です(もちろん、適切に#ifdefされたマクロ定義に抽象化されます) 。

4

4 に答える 4

10

コンパイル時に何かできるかどうかはわかりません。実行時チェックの場合、次のようにすることができます。

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

クライアントは次のように書く必要があります。

SaveMatrix sm(sm);

また、一時変数を識別子にバインドせずに同じことを行う方法はありません (その時点で、auto 変数と違いはありません)。

于 2009-09-22T23:35:37.760 に答える
6

SaveMatrix save();オブジェクトも定義しません。関数を宣言します。

他の人 (またはあなた自身、FTM) が望んでいないことをするのを防ぐためにできることはほとんどありません。私が考えることができる唯一のことは、コード自体を書くのではなく、代わりにマクロを書くことです.

#define SAVE_MATRIX SaveMatrix save ## __LINE__

しかし、これはかなり醜いです。OTOH、コンパイル時にエラーをキャッチします。

于 2009-09-22T23:31:41.070 に答える
3

実際には、Waldo が投稿したバリアントからさまざまな方法でソリューションを微調整する必要がありましたが、最終的にたどり着いたのは、次のマクロ化されたバージョンです。

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();
    }
};
于 2009-09-24T23:00:12.850 に答える
1

クラスは、それが一時 (SaveMatrix()) としてインスタンス化されたのか、変数 (SaveMatrix save;) としてインスタンス化されたのかわかりません。スタックやマクロのハックなしでプログラマーがそれを行うのを止める最善の方法は、構築後にメンバー関数呼び出しを強制することだと思います。

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

    void Do()
    {
        valid = true;
    }

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

これは次のように機能します。

{
    // 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;
}
于 2009-09-23T00:25:31.403 に答える