michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* Implementation of macros to ensure correct use of RAII Auto* objects. */ michael@0: michael@0: #ifndef mozilla_GuardObjects_h michael@0: #define mozilla_GuardObjects_h michael@0: michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/NullPtr.h" michael@0: #include "mozilla/Types.h" michael@0: michael@0: #ifdef __cplusplus michael@0: michael@0: #ifdef DEBUG michael@0: michael@0: namespace mozilla { michael@0: namespace detail { michael@0: michael@0: /* michael@0: * The following classes are designed to cause assertions to detect michael@0: * inadvertent use of guard objects as temporaries. In other words, michael@0: * when we have a guard object whose only purpose is its constructor and michael@0: * destructor (and is never otherwise referenced), the intended use michael@0: * might be: michael@0: * michael@0: * AutoRestore savePainting(mIsPainting); michael@0: * michael@0: * but is is easy to accidentally write: michael@0: * michael@0: * AutoRestore(mIsPainting); michael@0: * michael@0: * which compiles just fine, but runs the destructor well before the michael@0: * intended time. michael@0: * michael@0: * They work by adding (#ifdef DEBUG) an additional parameter to the michael@0: * guard object's constructor, with a default value, so that users of michael@0: * the guard object's API do not need to do anything. The default value michael@0: * of this parameter is a temporary object. C++ (ISO/IEC 14882:1998), michael@0: * section 12.2 [class.temporary], clauses 4 and 5 seem to assume a michael@0: * guarantee that temporaries are destroyed in the reverse of their michael@0: * construction order, but I actually can't find a statement that that michael@0: * is true in the general case (beyond the two specific cases mentioned michael@0: * there). However, it seems to be true. michael@0: * michael@0: * These classes are intended to be used only via the macros immediately michael@0: * below them: michael@0: * michael@0: * MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER declares (ifdef DEBUG) a member michael@0: * variable, and should be put where a declaration of a private michael@0: * member variable would be placed. michael@0: * MOZ_GUARD_OBJECT_NOTIFIER_PARAM should be placed at the end of the michael@0: * parameters to each constructor of the guard object; it declares michael@0: * (ifdef DEBUG) an additional parameter. (But use the *_ONLY_PARAM michael@0: * variant for constructors that take no other parameters.) michael@0: * MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL should likewise be used in michael@0: * the implementation of such constructors when they are not inline. michael@0: * MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT should be used in michael@0: * the implementation of such constructors to pass the parameter to michael@0: * a base class that also uses these macros michael@0: * MOZ_GUARD_OBJECT_NOTIFIER_INIT is a statement that belongs in each michael@0: * constructor. It uses the parameter declared by michael@0: * MOZ_GUARD_OBJECT_NOTIFIER_PARAM. michael@0: * michael@0: * For more details, and examples of using these macros, see michael@0: * https://developer.mozilla.org/en/Using_RAII_classes_in_Mozilla michael@0: */ michael@0: class GuardObjectNotifier michael@0: { michael@0: private: michael@0: bool* statementDone; michael@0: michael@0: public: michael@0: GuardObjectNotifier() : statementDone(nullptr) { } michael@0: michael@0: ~GuardObjectNotifier() { michael@0: *statementDone = true; michael@0: } michael@0: michael@0: void setStatementDone(bool* statementIsDone) { michael@0: statementDone = statementIsDone; michael@0: } michael@0: }; michael@0: michael@0: class GuardObjectNotificationReceiver michael@0: { michael@0: private: michael@0: bool statementDone; michael@0: michael@0: public: michael@0: GuardObjectNotificationReceiver() : statementDone(false) { } michael@0: michael@0: ~GuardObjectNotificationReceiver() { michael@0: /* michael@0: * Assert that the guard object was not used as a temporary. (Note that michael@0: * this assert might also fire if init is not called because the guard michael@0: * object's implementation is not using the above macros correctly.) michael@0: */ michael@0: MOZ_ASSERT(statementDone); michael@0: } michael@0: michael@0: void init(const GuardObjectNotifier& constNotifier) { michael@0: /* michael@0: * constNotifier is passed as a const reference so that we can pass a michael@0: * temporary, but we really intend it as non-const. michael@0: */ michael@0: GuardObjectNotifier& notifier = const_cast(constNotifier); michael@0: notifier.setStatementDone(&statementDone); michael@0: } michael@0: }; michael@0: michael@0: } /* namespace detail */ michael@0: } /* namespace mozilla */ michael@0: michael@0: #endif /* DEBUG */ michael@0: michael@0: #ifdef DEBUG michael@0: # define MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER \ michael@0: mozilla::detail::GuardObjectNotificationReceiver _mCheckNotUsedAsTemporary; michael@0: # define MOZ_GUARD_OBJECT_NOTIFIER_PARAM \ michael@0: , const mozilla::detail::GuardObjectNotifier& _notifier = \ michael@0: mozilla::detail::GuardObjectNotifier() michael@0: # define MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM \ michael@0: const mozilla::detail::GuardObjectNotifier& _notifier = \ michael@0: mozilla::detail::GuardObjectNotifier() michael@0: # define MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL \ michael@0: , const mozilla::detail::GuardObjectNotifier& _notifier michael@0: # define MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL \ michael@0: const mozilla::detail::GuardObjectNotifier& _notifier michael@0: # define MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT \ michael@0: , _notifier michael@0: # define MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_TO_PARENT \ michael@0: _notifier michael@0: # define MOZ_GUARD_OBJECT_NOTIFIER_INIT \ michael@0: do { _mCheckNotUsedAsTemporary.init(_notifier); } while (0) michael@0: #else michael@0: # define MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: # define MOZ_GUARD_OBJECT_NOTIFIER_PARAM michael@0: # define MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM michael@0: # define MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL michael@0: # define MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL michael@0: # define MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_TO_PARENT michael@0: # define MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT michael@0: # define MOZ_GUARD_OBJECT_NOTIFIER_INIT do { } while (0) michael@0: #endif michael@0: michael@0: #endif /* __cplusplus */ michael@0: michael@0: #endif /* mozilla_GuardObjects_h */