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: /* A number of structures to simplify scope-based RAII management. */ michael@0: michael@0: #ifndef mozilla_Scoped_h michael@0: #define mozilla_Scoped_h michael@0: michael@0: /* michael@0: * Resource Acquisition Is Initialization is a programming idiom used michael@0: * to write robust code that is able to deallocate resources properly, michael@0: * even in presence of execution errors or exceptions that need to be michael@0: * propagated. The Scoped* classes defined in this header perform the michael@0: * deallocation of the resource they hold once program execution michael@0: * reaches the end of the scope for which they have been defined. michael@0: * michael@0: * This header provides the following RAII classes: michael@0: * michael@0: * - |ScopedFreePtr| - a container for a pointer, that automatically calls michael@0: * |free()| at the end of the scope; michael@0: * - |ScopedDeletePtr| - a container for a pointer, that automatically calls michael@0: * |delete| at the end of the scope; michael@0: * - |ScopedDeleteArray| - a container for a pointer to an array, that michael@0: * automatically calls |delete[]| at the end of the scope. michael@0: * michael@0: * The general scenario for each of the RAII classes is the following: michael@0: * michael@0: * ScopedClass foo(create_value()); michael@0: * // ... In this scope, |foo| is defined. Use |foo.get()| or |foo.rwget()| michael@0: * to access the value. michael@0: * // ... In case of |return| or |throw|, |foo| is deallocated automatically. michael@0: * // ... If |foo| needs to be returned or stored, use |foo.forget()| michael@0: * michael@0: * Note that the RAII classes defined in this header do _not_ perform any form michael@0: * of reference-counting or garbage-collection. These classes have exactly two michael@0: * behaviors: michael@0: * michael@0: * - if |forget()| has not been called, the resource is always deallocated at michael@0: * the end of the scope; michael@0: * - if |forget()| has been called, any control on the resource is unbound michael@0: * and the resource is not deallocated by the class. michael@0: * michael@0: * Extension: michael@0: * michael@0: * In addition, this header provides class |Scoped| and macros |SCOPED_TEMPLATE| michael@0: * and |MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE| to simplify the definition michael@0: * of RAII classes for other scenarios. These macros have been used to michael@0: * automatically close file descriptors/file handles when reaching the end of michael@0: * the scope, graphics contexts, etc. michael@0: */ michael@0: michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/GuardObjects.h" michael@0: #include "mozilla/Move.h" michael@0: #include "mozilla/NullPtr.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: /* michael@0: * Scoped is a helper to create RAII wrappers michael@0: * Type argument |Traits| is expected to have the following structure: michael@0: * michael@0: * struct Traits { michael@0: * // Define the type of the value stored in the wrapper michael@0: * typedef value_type type; michael@0: * // Returns the value corresponding to the uninitialized or freed state michael@0: * const static type empty(); michael@0: * // Release resources corresponding to the wrapped value michael@0: * // This function is responsible for not releasing an |empty| value michael@0: * const static void release(type); michael@0: * } michael@0: */ michael@0: template michael@0: class Scoped michael@0: { michael@0: public: michael@0: typedef typename Traits::type Resource; michael@0: michael@0: explicit Scoped(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) michael@0: : value(Traits::empty()) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: } michael@0: michael@0: explicit Scoped(const Resource& v michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : value(v) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: } michael@0: michael@0: /* Move constructor. */ michael@0: explicit Scoped(Scoped&& v michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : value(Move(v.value)) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: v.value = Traits::empty(); michael@0: } michael@0: michael@0: ~Scoped() { michael@0: Traits::release(value); michael@0: } michael@0: michael@0: // Constant getter michael@0: operator const Resource&() const { return value; } michael@0: const Resource& operator->() const { return value; } michael@0: const Resource& get() const { return value; } michael@0: // Non-constant getter. michael@0: Resource& rwget() { return value; } michael@0: michael@0: /* michael@0: * Forget the resource. michael@0: * michael@0: * Once |forget| has been called, the |Scoped| is neutralized, i.e. it will michael@0: * have no effect at destruction (unless it is reset to another resource by michael@0: * |operator=|). michael@0: * michael@0: * @return The original resource. michael@0: */ michael@0: Resource forget() { michael@0: Resource tmp = value; michael@0: value = Traits::empty(); michael@0: return tmp; michael@0: } michael@0: michael@0: /* michael@0: * Perform immediate clean-up of this |Scoped|. michael@0: * michael@0: * If this |Scoped| is currently empty, this method has no effect. michael@0: */ michael@0: void dispose() { michael@0: Traits::release(value); michael@0: value = Traits::empty(); michael@0: } michael@0: michael@0: bool operator==(const Resource& other) const { michael@0: return value == other; michael@0: } michael@0: michael@0: /* michael@0: * Replace the resource with another resource. michael@0: * michael@0: * Calling |operator=| has the side-effect of triggering clean-up. If you do michael@0: * not want to trigger clean-up, you should first invoke |forget|. michael@0: * michael@0: * @return this michael@0: */ michael@0: Scoped& operator=(const Resource& other) { michael@0: return reset(other); michael@0: } michael@0: Scoped& reset(const Resource& other) { michael@0: Traits::release(value); michael@0: value = other; michael@0: return *this; michael@0: } michael@0: michael@0: /* Move assignment operator. */ michael@0: Scoped& operator=(Scoped&& rhs) { michael@0: MOZ_ASSERT(&rhs != this, "self-move-assignment not allowed"); michael@0: this->~Scoped(); michael@0: new(this) Scoped(Move(rhs)); michael@0: return *this; michael@0: } michael@0: michael@0: private: michael@0: explicit Scoped(const Scoped& value) MOZ_DELETE; michael@0: Scoped& operator=(const Scoped& value) MOZ_DELETE; michael@0: michael@0: private: michael@0: Resource value; michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: /* michael@0: * SCOPED_TEMPLATE defines a templated class derived from Scoped michael@0: * This allows to implement templates such as ScopedFreePtr. michael@0: * michael@0: * @param name The name of the class to define. michael@0: * @param Traits A struct implementing clean-up. See the implementations michael@0: * for more details. michael@0: */ michael@0: #define SCOPED_TEMPLATE(name, Traits) \ michael@0: template \ michael@0: struct name : public mozilla::Scoped > \ michael@0: { \ michael@0: typedef mozilla::Scoped > Super; \ michael@0: typedef typename Super::Resource Resource; \ michael@0: name& operator=(Resource rhs) { \ michael@0: Super::operator=(rhs); \ michael@0: return *this; \ michael@0: } \ michael@0: name& operator=(name&& rhs) { \ michael@0: Super::operator=(Move(rhs)); \ michael@0: return *this; \ michael@0: } \ michael@0: explicit name(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) \ michael@0: : Super(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_TO_PARENT) \ michael@0: {} \ michael@0: explicit name(Resource rhs \ michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) \ michael@0: : Super(rhs \ michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT) \ michael@0: {} \ michael@0: explicit name(name&& rhs \ michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) \ michael@0: : Super(Move(rhs) \ michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT) \ michael@0: {} \ michael@0: private: \ michael@0: explicit name(name&) MOZ_DELETE; \ michael@0: name& operator=(name&) MOZ_DELETE; \ michael@0: }; michael@0: michael@0: /* michael@0: * ScopedFreePtr is a RAII wrapper for pointers that need to be free()d. michael@0: * michael@0: * struct S { ... }; michael@0: * ScopedFreePtr foo = malloc(sizeof(S)); michael@0: * ScopedFreePtr bar = strdup(str); michael@0: */ michael@0: template michael@0: struct ScopedFreePtrTraits michael@0: { michael@0: typedef T* type; michael@0: static T* empty() { return nullptr; } michael@0: static void release(T* ptr) { free(ptr); } michael@0: }; michael@0: SCOPED_TEMPLATE(ScopedFreePtr, ScopedFreePtrTraits) michael@0: michael@0: /* michael@0: * ScopedDeletePtr is a RAII wrapper for pointers that need to be deleted. michael@0: * michael@0: * struct S { ... }; michael@0: * ScopedDeletePtr foo = new S(); michael@0: */ michael@0: template michael@0: struct ScopedDeletePtrTraits : public ScopedFreePtrTraits michael@0: { michael@0: static void release(T* ptr) { delete ptr; } michael@0: }; michael@0: SCOPED_TEMPLATE(ScopedDeletePtr, ScopedDeletePtrTraits) michael@0: michael@0: /* michael@0: * ScopedDeleteArray is a RAII wrapper for pointers that need to be delete[]ed. michael@0: * michael@0: * struct S { ... }; michael@0: * ScopedDeleteArray foo = new S[42]; michael@0: */ michael@0: template michael@0: struct ScopedDeleteArrayTraits : public ScopedFreePtrTraits michael@0: { michael@0: static void release(T* ptr) { delete [] ptr; } michael@0: }; michael@0: SCOPED_TEMPLATE(ScopedDeleteArray, ScopedDeleteArrayTraits) michael@0: michael@0: /* michael@0: * MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE makes it easy to create scoped michael@0: * pointers for types with custom deleters; just overload michael@0: * TypeSpecificDelete(T*) in the same namespace as T to call the deleter for michael@0: * type T. michael@0: * michael@0: * @param name The name of the class to define. michael@0: * @param Type A struct implementing clean-up. See the implementations michael@0: * for more details. michael@0: * *param Deleter The function that is used to delete/destroy/free a michael@0: * non-null value of Type*. michael@0: * michael@0: * Example: michael@0: * michael@0: * MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc, \ michael@0: * PR_Close) michael@0: * ... michael@0: * { michael@0: * ScopedPRFileDesc file(PR_OpenFile(...)); michael@0: * ... michael@0: * } // file is closed with PR_Close here michael@0: */ michael@0: #define MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(name, Type, Deleter) \ michael@0: template <> inline void TypeSpecificDelete(Type * value) { Deleter(value); } \ michael@0: typedef ::mozilla::TypeSpecificScopedPointer name; michael@0: michael@0: template void TypeSpecificDelete(T * value); michael@0: michael@0: template michael@0: struct TypeSpecificScopedPointerTraits michael@0: { michael@0: typedef T* type; michael@0: const static type empty() { return nullptr; } michael@0: const static void release(type value) michael@0: { michael@0: if (value) michael@0: TypeSpecificDelete(value); michael@0: } michael@0: }; michael@0: michael@0: SCOPED_TEMPLATE(TypeSpecificScopedPointer, TypeSpecificScopedPointerTraits) michael@0: michael@0: } /* namespace mozilla */ michael@0: michael@0: #endif /* mozilla_Scoped_h */