// bslmf_allocatorargt.h -*-C++-*- #ifndef INCLUDED_BSLMF_ALLOCATORARGT #define INCLUDED_BSLMF_ALLOCATORARGT #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a tag type to precede allocator arguments. // //@CLASSES: // bsl::allocator_arg_t: tag indicating the next parameter is an allocator // //@SEE_ALSO: bslalg_scalarprimitives // //@DESCRIPTION: The C++11 standard defines the empty class // 'bsl::allocator_arg_t' as a tag that precedes an argument of allocator type // in circumstances where context alone cannot be used to determine which // argument is an allocator. Typically, this disambiguation is needed when a // class has templated constructors taking variable numbers of arguments, each // of which is of parameterized type. If that class also uses an allocator (of // either STL style or 'bslma'/'memory_resource' style), then the allocator // argument must be flagged as special. An argument of type // 'std::allocator_arg_t' is used to distinguish constructors that take an // allocator from constructors that don't. // // In addition to the 'allocator_arg_t' class type, this component (and the // standard) define a constant, 'allocator_arg', of type 'allocator_arg_t'. // That constant is used for passing an 'allocator_arg_t' argument to a // function or constructor. // ///Usage ///----- // In this section we show intended use of this component. // ///Example 1: Disambiguate a constructor invocation /// - - - - - - - - - - - - - - - - - - - - - - - - // Suppose we want to define a nullable type that can be in the full state // (holding an object) or the null state (not holding an object). When in the // full state, memory is allocated for the held object using a memory // allocator. For simplicity, this memory allocator is not automatically // propagated to the held object. // // First, we define a simple allocator class hierarchy with an abstract // 'xyzma::Allocator' base class and two derived classes: // 'xyzma::NewDeleteAllocator' and 'xyzma::TestAllocator': //.. // #include <cstddef> // // namespace xyzma { // // class Allocator { // // Abstract allocator base class // public: // virtual ~Allocator() { } // // virtual void *allocate(std::size_t nbytes) = 0; // virtual void deallocate(void *ptr) = 0; // }; // // class NewDeleteAllocator : public Allocator { // // Concrete allocator that uses operators 'new' and 'delete' // // public: // static NewDeleteAllocator* singleton(); // // Returns a singleton instance of this class // // virtual void *allocate(std::size_t nbytes); // virtual void deallocate(void *ptr); // }; // // NewDeleteAllocator *NewDeleteAllocator::singleton() { // static NewDeleteAllocator s; // return &s; // } // // void *NewDeleteAllocator::allocate(std::size_t nbytes) { // return ::operator new(nbytes); // } // // void NewDeleteAllocator::deallocate(void *ptr) { // ::operator delete(ptr); // } // // class TestAllocator : public Allocator { // // Concrete allocator that keeps track of number of blocks allocated // // and deallocated. // // std::size_t d_allocatedBlocks; // std::size_t d_deallocatedBlocks; // // public: // TestAllocator() : d_allocatedBlocks(0), d_deallocatedBlocks(0) { } // // virtual void *allocate(std::size_t nbytes); // virtual void deallocate(void *ptr); // // // ACCESSORS // std::size_t allocatedBlocks() const { return d_allocatedBlocks; } // std::size_t deallocatedBlocks() const { return d_deallocatedBlocks; } // std::size_t outstandingBlocks() const { // return d_allocatedBlocks - d_deallocatedBlocks; // } // }; // // void *TestAllocator::allocate(std::size_t nbytes) { // ++d_allocatedBlocks; // return ::operator new(nbytes); // } // // void TestAllocator::deallocate(void *ptr) { // ++d_deallocatedBlocks; // ::operator delete(ptr); // } // // } // close namespace xyzma //.. // Next, we define our nullable class template, declaring two constructors: one // that constructs the null object, and one that constructs a non-null object // using the specified constructor argument. For flexibility, the second // constructor is a template that takes any type and can therefore construct // the object without necessarily invoking the copy constructor. (Ideally, // this second constructor would be variadic, but that is not necessary for // this example.): //.. // #include <new> // // namespace xyzutl { // // template <class TYPE> // class Nullable { // xyzma::Allocator *d_alloc_p; // TYPE *d_object_p; // // public: // // CREATORS // Nullable(); // // Construct a null object. Note: this is ctor A. // // template <class ARG> // Nullable(const ARG& arg); // // Construct a non-null object using the specified 'arg' as the // // constructor argument for the 'TYPE' object. Note: this is ctor // // B. //.. // Next, we want to add constructors that supply an allocator for use by the // 'Nullable' object. Our first thought is to add two more constructors like // the two above, but with an additional allocator argument at the end: //.. // // Nullable(xyzma::Allocator *alloc); // // ctor C // // // template <class ARG> // // Nullable(const ARG& arg, xyzma::Allocator *alloc); // // ctor D //.. // However, ctor C is difficult to invoke, because ctor B is almost always a // better match. Nor can we use SFINAE to disqualify ctor B in cases where // ARG is 'xyzma::Allocator*' because 'xyzma::Allocator*' is a perfectly valid // constructor argument for many 'TYPE's. // // We solve this problem by using 'allocator_arg_t' to explicitly tag the // constructor that takes an allocator argument: //.. // Nullable(bsl::allocator_arg_t, xyzma::Allocator *alloc); // // Construct a null object with the specified 'alloc' allocator. // // Note: this is ctor E //.. // The 'allocator_arg_t' argument disambiguates the constructor. // // Next, to make things consistent (which is important for generic // programming), we use the 'allocator_arg_t' tag in the other allocator-aware // constructor, as well: //.. // template <class ARG> // Nullable(bsl::allocator_arg_t, // xyzma::Allocator *alloc, // const ARG& arg); // // Construct a non-null object using the specified 'arg' as the // // constructor argument for the 'TYPE' object, and the specified // // 'alloc' allocator. Note: this is ctor F. //.. // Next, we finish the class interface and implementation: //.. // ~Nullable(); // // // MANIPULATORS // Nullable& operator=(const Nullable& rhs); // // Copy assign this object from the specified 'rhs'. // // Nullable& operator=(const TYPE& rhs); // // Construct a non-null object holding a copy of the specified // // 'rhs' object. // // // ACCESSORS // const TYPE& value() const { return *d_object_p; } // // Return the object stored in this Nullable. The behavior is // // undefined if this is null. // // bool isNull() const { return ! d_object_p; } // // Returns true if this object is not null. // // xyzma::Allocator *allocator() const { return d_alloc_p; } // }; // // template <class TYPE> // Nullable<TYPE>::Nullable() // : d_alloc_p(xyzma::NewDeleteAllocator::singleton()) // , d_object_p(0) // { // } // // template <class TYPE> // template <class ARG> // Nullable<TYPE>::Nullable(const ARG& arg) // : d_alloc_p(xyzma::NewDeleteAllocator::singleton()) // , d_object_p(static_cast<TYPE*>(d_alloc_p->allocate(sizeof(TYPE)))) // { // ::new(d_object_p) TYPE(arg); // } // // template <class TYPE> // Nullable<TYPE>::Nullable(bsl::allocator_arg_t, xyzma::Allocator *alloc) // : d_alloc_p(alloc) // , d_object_p(0) // { // } // // template <class TYPE> // template <class ARG> // Nullable<TYPE>::Nullable(bsl::allocator_arg_t, // xyzma::Allocator *alloc, // const ARG& arg) // : d_alloc_p(alloc) // , d_object_p(static_cast<TYPE*>(d_alloc_p->allocate(sizeof(TYPE)))) // { // ::new(d_object_p) TYPE(arg); // } // // template <class TYPE> // Nullable<TYPE>::~Nullable() { // if (d_object_p) { // d_object_p->~TYPE(); // d_alloc_p->deallocate(d_object_p); // } // } // // template <class TYPE> // Nullable<TYPE>& Nullable<TYPE>::operator=(const Nullable& rhs) { // if (&rhs == this) return *this; // RETURN // if (!isNull() && !rhs.isNull()) { // *d_object_p = *rhs.d_object_p; // } // else if (!isNull() /* && rhs.isNull() */) { // // Make null // d_object_p->~TYPE(); // d_alloc_p->deallocate(d_object_p); // d_object_p = 0; // } // else if (/* isNull() && */ !rhs.isNull()) { // // Allocate and copy from 'rhs' // d_object_p = static_cast<TYPE*>(d_alloc_p->allocate(sizeof(TYPE))); // ::new(d_object_p) TYPE(*rhs.d_object_p); // } // // else both are null // // return *this; // } // // template <class TYPE> // Nullable<TYPE>& Nullable<TYPE>::operator=(const TYPE& rhs) { // if (isNull()) { // d_object_p = static_cast<TYPE*>(d_alloc_p->allocate(sizeof(TYPE))); // ::new(d_object_p) TYPE(*rhs.d_object_p); // } // else { // *d_object_p = rhs; // } // // return *this; // } // // } // close namespace xyzutl //.. // Now, for testing purposes, we define a class that takes an allocator // constructor argument: //.. // class Obj { // xyzma::Allocator *d_alloc_p; // int d_count; // public: // explicit Obj(xyzma::Allocator *alloc = 0) // : d_alloc_p(alloc), d_count(0) // { // } // // Obj(int count, xyzma::Allocator *alloc = 0) // IMPLICIT // : d_alloc_p(alloc), d_count(count) // { // } // // int count() const { return d_count; } // xyzma::Allocator *allocator() const { return d_alloc_p; } // }; // // bool operator==(const Obj& a, const Obj& b) { // return a.count() == b.count(); // } // // bool operator!=(const Obj& a, const Obj& b) { // return a.count() != b.count(); // } //.. // Finally, we test that our nullable type can be constructed with and without // an allocator pointer and that the allocator pointer can unambiguously be // used for the object's allocator. //.. // int main() { // // using xyzutl::Nullable; // // xyzma::TestAllocator ta; // // Nullable<Obj> no1; // assert( no1.isNull()); // assert(xyzma::NewDeleteAllocator::singleton() == no1.allocator()); // // Nullable<Obj> no2(2); // assert(! no2.isNull()); // assert(xyzma::NewDeleteAllocator::singleton() == no2.allocator()); // assert(2 == no2.value()); // assert(0 == no2.value().allocator()); // // Nullable<Obj> no3(bsl::allocator_arg, &ta); // assert( no3.isNull()); // assert(&ta == no3.allocator()); // assert(0 == ta.outstandingBlocks()); // // Nullable<Obj> no4(bsl::allocator_arg, &ta, 4); // assert(! no4.isNull()); // assert(&ta == no4.allocator()); // assert(1 == ta.outstandingBlocks()); // assert(4 == no4.value()); // assert(0 == no4.value().allocator()); // // // '&ta' used by 'Obj', not by 'Nullable'. // Nullable<Obj> no5(&ta); // assert(! no5.isNull()); // assert(xyzma::NewDeleteAllocator::singleton() == no5.allocator()); // assert(1 == ta.outstandingBlocks()); // No change // assert(0 == no5.value()); // assert(&ta == no5.value().allocator()); // // // '&ta' used by both 'Nullable' and by 'Obj' // Nullable<Obj> no6(bsl::allocator_arg, &ta, &ta); // assert(! no6.isNull()); // assert(&ta == no6.allocator()); // assert(2 == ta.outstandingBlocks()); // assert(0 == no6.value()); // assert(&ta == no6.value().allocator()); // // return 0; // } //.. #include <bslscm_version.h> namespace bsl { // ====================== // struct allocator_arg_t // ====================== struct allocator_arg_t { // Tag type indicating that next argument is an allocator. // TBD: If native library declares 'std::allocator_arg_t', then this // struct should be an replaced by an alias for 'std::allocator_arg_t'. }; static const allocator_arg_t allocator_arg = { }; // Value of type 'allocator_arg_t' used as actual argument to function // that takes an allocator argument. Note that 'constexpr' would be better // than 'const' here, but any compiler that supports 'constexpr' would also // provide this class and named value, that should be imported into // namespace 'bsl' with using declarations instead. } // close namespace bsl #endif // ! defined(INCLUDED_BSLMF_ALLOCATORARGT) // ---------------------------------------------------------------------------- // Copyright 2015 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 ----------------------------------