Search code examples
c++gccvisual-c++raiiicc

Is it possible to prevent an RAII-style class from being instantiated "anonymously"?


Suppose I have an RAII-style C++ class:

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

...to be used like so in my code:

void Manipulate()
{
  StateSaver save(1);

  // ...do stuff that modifies state
}

...the goal being to enter some state, do stuff, then leave that state when I leave that scope. Is there a way to make this typo not compile (or warn, or somehow complain so that the mistake can be noticed)?

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

  // ...do stuff that modifies state
}

I'm not aware of anything in C++ itself which I could use to prevent this, but that doesn't mean it doesn't exist. If there isn't anything in C++, compiler-specific extensions would be acceptable. I'm primarily interested in anything targeting gcc and msvc (someday icc, ideas for other compilers welcome but less likely to be useful) so hacks for any of them would be useful (abstracted into appropriately #ifdef'd macro definitions, of course).


Solution

  • I actually had to tweak my solution in a bunch of ways from the variant Waldo posted, but what I eventually got to is a macro-ized version of:

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