// bslma_allocator.h -*-C++-*- #ifndef INCLUDED_BSLMA_ALLOCATOR #define INCLUDED_BSLMA_ALLOCATOR #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a pure abstract interface for memory-allocation mechanisms. // //@CLASSES: // bslma::Allocator: protocol class for memory allocation and deallocation // //@SEE_ALSO: bslma_newdeleteallocator, bslma_testallocator // //@DESCRIPTION: This component provides the base-level protocol (pure abstract // interface) class, 'bslma_allocator', that serves as a ubiquitous vocabulary // type for various memory allocation mechanisms. The functional capabilities // documented by this protocol are similar to those afforded by global // operators 'new' and 'delete': sufficiently (but not necessarily maximally) // aligned memory is guaranteed for any object of a given size. Clients of // this abstract base class will typically accept a supplied allocator (often // at construction) and use its 'allocate' and 'deallocate' methods instead of // 'new' and 'delete' directly. // // The use of (abstract) allocators provides at least three distinct advantages // over direct (hard-coded) calls to global 'new' and 'delete' (see // 'bslma_newdeleteallocator'): // //: 1 The particular choice of allocator can be selected to improve performance //: on a per-object basis. Without allocators, the best we can do in C++ is //: to overload the class-specific new and delete. Class-specific allocators //: tend to hoard memory even when most objects of the class have been //: deallocated, and often mask memory leaks that would otherwise have been //: detected. See Lakos-96, Section 10.3.4.2, pp 705-711. //: //: 2 By providing extra capabilities (beyond 'new' and 'delete') in the //: derived class (see 'bslma_managedallocator'), we can bypass the //: individual destructors in a dynamically allocated type and remove all //: memory for one or more associated object almost instantly. //: //: 3 The 'bslma::Allocator' protocol, like any other protocol, isolates //: clients from direct coupling with platform level facilities that are not //: fully under their control. By installing a test allocator (see //: 'bslma_testallocator'), we are able to orchestrate the white-box testing //: of internal calls to global operators 'new' and 'delete' in a //: platform-neutral manner. // ///Thread Safety ///------------- // Unless otherwise documented, a single allocator object is not safe for // concurrent access by multiple threads. Classes derived from // 'bslma::Allocator' that are specifically designed for concurrent access must // be documented as such. Unless specifically documented otherwise, separate // objects of classes derived from 'bslma::Allocator' may safely be used in // separate threads. // ///Allocators Versus Pools ///----------------------- // An allocator and a pool are quite different. For starters, // 'bslma::Allocator' is an abstract class used to obtain "raw" memory of // arbitrary size. A pool is a concrete data structure used to organize and // supply memory according to specific needs (e.g., a consistent size). // Concrete allocators may use pools in their implementations, and pools will // always take a base 'bslma::Allocator' protocol in their interface. You can // think of an allocator as a stream of memory that flows into a pool of // memory. Memory is allocated from the pool until it is dry; only then does // new memory flow into the pool from the allocator. // ///Overloaded Global Operators 'new' and 'delete' ///---------------------------------------------- // This component overloads the global operator 'new' to allow convenient // syntax for the construction of objects using the 'bslma::Allocator' // protocol. The overloaded 'new' operator defined in this component has a // second parameter, 'bslma::Allocator&', that identifies the concrete // (derived) allocator that will be used to supply memory. // // Consider the following use of standard placement syntax (supplied by // '#include <new>') along with a 'bslma::Allocator', used to allocate an // arbitrary 'TYPE'. //.. // void someFunction(bslma::Allocator *basicAllocator) // { // TYPE *obj = new (basicAllocator->allocate(sizeof(TYPE))) TYPE(...); // // // ... //.. // This style of usage is inconvenient and error prone; it is also *not* // exception safe: If the constructor of 'TYPE' throws an exception, the // 'basicAllocator->deallocate' method is never called. // // Providing an overloaded global operator 'new', taking a reference to a // modifiable 'bslma::Allocator' as an explicit argument allows for cleaner // usage and guarantees that the 'basicAllocator->deallocate' method is called // in case of an exception: //.. // void someFunction(bslma::Allocator *basicAllocator) // { // TYPE *obj = new (*basicAllocator) TYPE(...); // // // ... //.. // Finally, the analogous version of operator 'delete' should not be called // directly: The overloaded operator 'delete' supplied in this component is // solely for the compiler to invoke in the event an exception is thrown during // a failed construction. Instead, the 'bslma::Allocator' protocol provides // 'deleteObject' (a template member function parameterized by the type of the // object being deleted), which is implemented *conceptually* as follows: //.. // template <class TYPE> // void bslma::Allocator::deleteObject(TYPE *address) // { // address->~TYPE(); // this->deallocate(address); // } //.. // Note that there is also a 'deleteObjectRaw' which is more efficient when it // is known that the 'address' does *not* refer to a secondary base class of // the object being deleted. // ///Usage ///----- // The 'bslma::Allocator' protocol provided in this component defines a // bilateral contract between suppliers and consumers of raw memory. The // following subsections illustrate (1) use, and (2) implementation of the // abstract 'bslma::Allocator' base class: // ///Example 1: Container Objects /// - - - - - - - - - - - - - - // Allocators are often supplied to objects requiring dynamically-allocated // memory at construction. For example, consider the following // 'my_DoubleStack' class, parameterized by a 'bslma::Allocator': //.. // // my_doublestack.h // // ... // // namespace bslma { class Allocator; } // forward declaration of allocator // // class my_DoubleStack { // // DATA // double *d_stack_p; // dynamically allocated array (d_size // // elements) // // int d_size; // physical capacity of this stack (in // // elements) // // int d_length; // logical index of next available // // stack element // // bslma::Allocator *d_allocator_p; // holds (but does not own) object // // // FRIENDS // friend class my_DoubleStackIter; // // private: // // PRIVATE MANIPULATORS // void increaseSize(); // Increase the capacity by at least one element. // // public: // // CREATORS // my_DoubleStack(bslma::Allocator *basicAllocator = 0); // my_DoubleStack(const my_DoubleStack& other, // bslma::Allocator *basicAllocator = 0); // ~my_DoubleStack(); // // // MANIPULATORS // my_DoubleStack& operator=(const my_DoubleStack& rhs); // void push(double value); // void pop(); // // // ACCESSORS // const double& top() const; // bool isEmpty() const; // }; // // // ... // // inline // void my_DoubleStack::push(double value) // { // if (d_length >= d_size) { // increaseSize(); // } // d_stack_p[d_length++] = item; // } // // // ... //.. // The stack interface takes an optional 'basicAllocator' supplied only at // construction. (We avoid use of the name 'allocator' so as not to conflict // with the STL use of the word, which differs slightly.) If non-zero, the // stack holds a pointer to this allocator, but does not own it. If no // allocator is supplied, the implementation itself must either conditionally // invoke global 'new' and 'delete' explicitly whenever dynamic memory must be // managed (BAD IDEA) or (GOOD IDEA) install a default allocator that adapts // use of these global operators to the 'bslma_allocator' interface (see // 'bslma_newdeleteallocator'). //.. // // my_doublestack.cpp // #include <my_doublestack.h> // #include <bslma_allocator.h> // #include <bslma_newdeleteallocator.h> // adapter for 'new' and 'delete' // // ... // // enum { INITIAL_SIZE = 1, GROW_FACTOR = 2 }; // // // ... // // // CREATORS // my_DoubleStack::my_DoubleStack(bslma::Allocator *basicAllocator) // : d_size(INITIAL_SIZE) // , d_length(0) // , d_allocator_p(bslma::NewDeleteAllocator::allocator(basicAllocator)) // // The above initialization expression is equivalent to 'basicAllocator // // ? basicAllocator : &bslma::NewDeleteAllocator::singleton()'. // { // assert(d_allocator_p); // d_stack_p = (double *) // d_allocator_p->allocate(d_size * sizeof *d_stack_p); // } // // my_DoubleStack::~my_DoubleStack() // { // // CLASS INVARIANTS // assert(d_allocator_p); // assert(d_stack_p); // assert(0 <= d_length); // assert(0 <= d_size); // assert(d_length <= d_size); // // d_allocator_p->deallocate(d_stack_p); // } //.. // Even in this simplified implementation, all use of the allocator protocol is // relegated to the '.cpp' file. Subsequent use of the allocator is // demonstrated by the following file-scope static reallocation function: //.. // static inline // void reallocate(double **array, // int newSize, // int length, // bslma::Allocator *basicAllocator) // // Reallocate memory in the specified 'array' to the specified // // 'newSize' using the specified 'basicAllocator'. The specified // // 'length' number of leading elements are preserved. Since the // // class invariant requires that the physical capacity of the // // container may grow but never shrink, the behavior is undefined // // unless 'length <= newSize'. // { // assert(array); // assert(1 <= newSize); // assert(0 <= length); // assert(basicAllocator); // assert(length <= newSize); // enforce class invariant // // double *tmp = *array; // support exception neutrality // *array = (double *) basicAllocator->allocate(newSize * sizeof **array); // // // COMMIT POINT // // std::memcpy(*array, tmp, length * sizeof **array); // basicAllocator->deallocate(tmp); // } // // // PRIVATE MANIPULATORS // void my_DoubleStack::increaseSize() // { // int proposedNewSize = d_size * GROW_FACTOR; // reallocate can throw // assert(proposedNewSize > d_length); // reallocate(&d_stack_p, proposedNewSize, d_length, d_allocator_p); // d_size = proposedNewSize; // we're committed // } //.. // ///Example 2: Derived Concrete Allocators /// - - - - - - - - - - - - - - - - - - - // In order for the 'bslma::Allocator' interface to be useful, we must supply a // concrete allocator that implements it. In this example we demonstrate how // to adapt 'operator new' and 'operator delete' to this protocol base class. //.. // // my_newdeleteallocator.h // // ... // // class my_NewDeleteAllocator : public bslma::Allocator { // // This class is a sample concrete implementation of the // // 'bslma::Allocator' protocol that provides direct access to the // // system-supplied (native) global operators 'new' and 'delete'. // // // NOT IMPLEMENTED // my_NewDeleteAllocator(const bslma::NewDeleteAllocator&); // my_NewDeleteAllocator& operator=(const bslma::NewDeleteAllocator&); // // public: // // CREATORS // my_NewDeleteAllocator(); // // Create an allocator that wraps the global (native) operators // // 'new' and 'delete' to supply memory. Note that all objects of // // this class share the same underlying resource. // // virtual ~my_NewDeleteAllocator(); // // Destroy this allocator object. Note that destroying this // // allocator has no effect on any outstanding allocated memory. // // // MANIPULATORS // virtual void *allocate(size_type size); // // Return a newly allocated block of memory of (at least) the // // specified positive 'size' (in bytes). If 'size' is 0, a null // // pointer is returned with no other effect. If this allocator // // cannot return the requested number of bytes, then it will throw // // a 'std::bad_alloc' exception in an exception-enabled build, or // // else will abort the program in a non-exception build. The // // behavior is undefined unless '0 <= size'. Note that the // // alignment of the address returned is the maximum alignment for // // any type defined on this platform. Also note that global // // 'operator new' is *not* called when 'size' is 0 (in order to // // avoid having to acquire a lock, and potential contention in // // multi-threaded programs). // // virtual void deallocate(void *address); // // Return the memory block at the specified 'address' back to this // // allocator. If 'address' is 0, this function has no effect. The // // behavior is undefined unless 'address' was allocated using this // // allocator object and has not already been deallocated. Note // // that global 'operator delete' is *not* called when 'address' is // // 0 (in order to avoid having to acquire a lock, and potential // // contention in multi-treaded programs). // }; // // // CREATORS // inline // my_NewDeleteAllocator::my_NewDeleteAllocator() // { // } // // ... //.. // The virtual methods of 'my_NewDeleteAllocator' are defined in the component // '.cpp' file as they would not be inlined when invoked from the base class, // which would be the typical usage in this case: //.. // // my_newdeleteallocator.cpp // #include <my_newdeleteallocator.h> // // // CREATORS // my_NewDeleteAllocator::~my_NewDeleteAllocator() // { // } // // // MANIPULATORS // void *my_NewDeleteAllocator::allocate(size_type size) // { // BSLS_ASSERT_SAFE(0 <= size); // // return 0 == size ? 0 : ::operator new(size); // } // // void my_NewDeleteAllocator::deallocate(void *address) // { // // While the C++ standard guarantees that calling delete(0) is safe // // (3.7.3.2 paragraph 3), some libc implementations take out a lock to // // deal with the free(0) case, so this check can improve efficiency of // // threaded programs. // // if (address) { // ::operator delete(address); // } // } //.. #include <bslscm_version.h> #include <bslma_deleterhelper.h> #include <bslmf_assert.h> #include <bslmf_isbitwiseequalitycomparable.h> #include <bslmf_isbitwisemoveable.h> #include <bslmf_issame.h> #include <bslmf_istriviallycopyable.h> #include <bslmf_nestedtraitdeclaration.h> #include <bsls_keyword.h> #include <bsls_nullptr.h> #include <bsls_platform.h> #include <bsls_types.h> #include <cstddef> // for 'std::size_t', 'std::ptrdiff_t' #ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES #include <bsls_cpp11.h> #endif namespace BloombergLP { namespace bslma { // =============== // class Allocator // =============== class Allocator { // This protocol class provides a pure abstract interface and contract for // clients and suppliers of raw memory. If the requested memory cannot be // returned, the contract requires that an 'std::bad_alloc' exception be // thrown. Note that memory is guaranteed to be sufficiently aligned for // any object of the requested size on the current platform, which may be // less than the maximal alignment guarantee afforded by global // 'operator new'. public: // PUBLIC TYPES typedef bsls::Types::size_type size_type; // Alias for a signed integral type capable of representing the number // of bytes in this platform's virtual address space. #ifndef BDE_OMIT_INTERNAL_DEPRECATED // CLASS METHODS static void throwBadAlloc(); // !DEPRECATED!: Use 'bsls::BslExceptionUtil::throwBadAlloc' instead. // // Throw 'std::bad_alloc' if exceptions are enabled or abort the // program otherwise. Derived classes and helper functions will // typically call this function when they are unable to satisfy an // allocation request. This function never returns. #endif // BDE_OMIT_INTERNAL_DEPRECATED // CREATORS virtual ~Allocator(); // Destroy this allocator. Note that the behavior of destroying an // allocator while memory is allocated from it is not specified. // (Unless you *know* that it is valid to do so, don't!) // MANIPULATORS virtual void *allocate(size_type size) = 0; // Return a newly allocated block of memory of (at least) the specified // positive 'size' (in bytes). If 'size' is 0, a null pointer is // returned with no other effect. If this allocator cannot return the // requested number of bytes, then it will throw a 'std::bad_alloc' // exception in an exception-enabled build, or else will abort the // program in a non-exception build. The behavior is undefined unless // '0 <= size'. Note that the alignment of the address returned // conforms to the platform requirement for any object of the specified // 'size'. virtual void deallocate(void *address) = 0; // Return the memory block at the specified 'address' back to this // allocator. If 'address' is 0, this function has no effect. The // behavior is undefined unless 'address' was allocated using this // allocator object and has not already been deallocated. template <class TYPE> void deleteObject(const TYPE *object); // Destroy the specified 'object' based on its dynamic type and then // use this allocator to deallocate its memory footprint. Do nothing // if 'object' is a null pointer. The behavior is undefined unless // 'object', when cast appropriately to 'void *', was allocated using // this allocator and has not already been deallocated. Note that // 'dynamic_cast<void *>(object)' is applied if 'TYPE' is polymorphic, // and 'static_cast<void *>(object)' is applied otherwise. template <class TYPE> void deleteObjectRaw(const TYPE *object); // Destroy the specified 'object' and then use this allocator to // deallocate its memory footprint. Do nothing if 'object' is a null // pointer. The behavior is undefined unless 'object' was allocated // using this allocator, is !not! a secondary base class pointer -- // i.e., the address is (numerically) the same as when it was // originally dispensed by this allocator, and has not already been // deallocated. void deleteObject(bsl::nullptr_t); // This function has no effect. Note that it exists to support calling // 'deleteObject' will a null pointer literal, that would otherwise not // deduce to a pointer type for the method above. As calls to // 'deleteObject' with (typed) null pointer values have well-defined // behavior, it should also support calls with a null pointer literal. void deleteObjectRaw(bsl::nullptr_t); // This function has no effect. Note that it exists to support calling // 'deleteObjectRaw' will a null pointer literal, that would otherwise // not deduce to a pointer type for the method above. As calls to // 'deleteObjectRaw' with (typed) null pointer values have well-defined // behavior, it should also support calls with a null pointer literal. }; } // close package namespace } // close enterprise namespace // FREE OPERATORS // Note that the operators 'new' and 'delete' are declared outside the // 'BloombergLP' namespace so that they do not hide the standard placement // 'new' and 'delete' operators (i.e., // 'void *operator new(std::size_t, void *)' and // 'void operator delete(void *)'). // // Note also that only the scalar versions of operators 'new' and 'delete' are // provided, because overloading 'new' (and 'delete') with their array versions // would cause dangerous ambiguity. Consider what would have happened had we // overloaded the array version of operator 'new': //.. // void *operator new[](std::size_t size, // BloombergLP::bslma::Allocator& basicAllocator) //.. // The user of the allocator class may expect to be able to use array // 'operator new' as follows: //.. // new (*basicAllocator) my_Type[...]; //.. // The problem is that this expression returns an array that cannot be safely // deallocated. On the one hand, there is no syntax in C++ to invoke an // overloaded 'operator delete' that, other than deallocating memory, would // invoke the destructor. On the other hand, the pointer returned by // 'operator new' cannot be passed to the 'deallocate' method directly because // the pointer is different from the one returned by the 'allocate' method. // The compiler offsets the value of this pointer by a header, which is used to // maintain the number of objects in the array (so that the non-overloaded // 'operator delete' can destroy the right number of objects). inline void *operator new(std::size_t size, BloombergLP::bslma::Allocator& basicAllocator); // Return the memory allocated from the specified 'basicAllocator' of at // least the specified 'size' bytes, or 0 if 'size' is 0. The behavior is // undefined unless '0 <= static_cast<bslma::Allocator::size_type>(size)'. // Note that an object may allocate additional memory internally, requiring // the allocator to be passed in as a constructor argument: //.. // my_Type *createMyType(bslma::Allocator *basicAllocator) // { // return new (*basicAllocator) my_Type(..., basicAllocator); // } //.. // Note also that the analogous version of operator 'delete' should *not* // be called directly. Instead, this component provides a template member // function 'deleteObject' parameterized by 'TYPE' that effectively // performs the following operations: //.. // void deleteMyType(bslma::Allocator *basicAllocator, my_Type *address) // { // address->~my_Type(); // basicAllocator->deallocate(address); // } //.. // See also 'deleteObjectRaw' for better performance when 'address' is // known not to be a secondary base type of the object being deleted. inline void operator delete(void *address, BloombergLP::bslma::Allocator& basicAllocator); // Use the specified 'basicAllocator' to deallocate the memory at the // specified 'address'. The behavior is undefined unless 'address' was // allocated using 'basicAllocator' and has not already been deallocated. // This operator is supplied solely to allow the compiler to arrange for it // to be called in case of an exception. // NOTE: The following two operators are declared but never defined to force a // link-time error should any code inadvertently use them. void *operator new( std::size_t size, BloombergLP::bslma::Allocator *basicAllocator) BSLS_KEYWORD_DELETED; // Note that this operator is intentionally not defined. void operator delete( void *address, BloombergLP::bslma::Allocator *basicAllocator) BSLS_KEYWORD_DELETED; // Note that this operator is intentionally not defined. // ============================================================================ // INLINE DEFINITIONS // ============================================================================ namespace BloombergLP { namespace bslma { // --------------- // class Allocator // --------------- // MANIPULATORS template <class TYPE> inline void Allocator::deleteObject(const TYPE *object) { DeleterHelper::deleteObject(object, this); } inline void Allocator::deleteObject(bsl::nullptr_t) { // This function body is intentionally left blank. } template <class TYPE> inline void Allocator::deleteObjectRaw(const TYPE *object) { DeleterHelper::deleteObjectRaw(object, this); } inline void Allocator::deleteObjectRaw(bsl::nullptr_t) { // This function body is intentionally left blank. } } // close package namespace #ifndef BDE_OPENSOURCE_PUBLICATION // BACKWARD_COMPATIBILITY // ============================================================================ // BACKWARD COMPATIBILITY // ============================================================================ typedef bslma::Allocator bslma_Allocator; // This alias is defined for backward compatibility. #ifndef bdema_Allocator #define bdema_Allocator bslma::Allocator #endif #endif // BDE_OPENSOURCE_PUBLICATION -- BACKWARD_COMPATIBILITY } // close enterprise namespace // ============================================================================ // INLINE FUNCTION DEFINITIONS // ============================================================================ inline void *operator new(std::size_t size, BloombergLP::bslma::Allocator& basicAllocator) { return basicAllocator.allocate(size); } inline void operator delete(void *address, BloombergLP::bslma::Allocator& basicAllocator) { basicAllocator.deallocate(address); } #endif // ---------------------------------------------------------------------------- // Copyright 2013 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------