// bsls_objectbuffer.h -*-C++-*- #ifndef INCLUDED_BSLS_OBJECTBUFFER #define INCLUDED_BSLS_OBJECTBUFFER #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide raw buffer with size and alignment of user-specified type. // //@CLASSES: // bsls::ObjectBuffer: parameterized buffer aligned to hold specified type // //@SEE_ALSO: bsls_alignmentfromtype // //@DESCRIPTION: This component provides a templated buffer type, // 'bsls::ObjectBuffer', that is compile-time sized and aligned to hold a // specified object type. Defining a 'bsls::ObjectBuffer<TYPE>' object does // not cause the constructor for 'TYPE' to be called. Similarly, destroying // the object buffer does not call the destructor for 'TYPE'. Instead, the // user instantiates 'bsls::ObjectBuffer' with a specific type, then constructs // an object of that type within that buffer. When the object is no longer // needed, the user must explicitly call its destructor. A // 'bsls::ObjectBuffer' can reside on the stack or within another object, // including within a 'union'. // // Typically, a 'bsls::ObjectBuffer' is used in situations where efficient // (e.g., stack-based) storage is required but where straightforward // initialization or destruction of an object is not possible. For example, // 'bsls::ObjectBuffer' can be used to construct an array where the number of // used elements varies at run-time or where the element type does not have a // default constructor. It can also be used to create a 'union' containing // non-POD element types. // ///Usage ///----- // The examples below use a value-semantic string class, 'my_String', that can // be constructed from a null-terminated string and contains a member, 'c_str', // that returns a null-terminated string. 'my_String' does not have a default // constructor and thus cannot be used in C-style arrays or unions. // ///Example 1: Creating a Dynamic Array of Objects /// - - - - - - - - - - - - - - - - - - - - - - - // Here we use 'bsls::ObjectBuffer' to create a variable-length array of // 'my_String' objects. For efficiency, the array is created on the stack as // a fixed-sized array of 'bsls::ObjectBuffer<my_String>' objects and the // length is kept in a separate variable. Only 'len' calls are made to the // 'my_String' constructor, with the unused array elements left as raw // memory. An array directly containing 'my_String' objects would not have // been possible because 'my_String' does not have a default constructor. // // WARNING: the 'manipulateStrings' function below is not exception-safe. // If an exception is thrown anywhere within the function (e.g., from a // constructor call), the destructor will not be called on the constructed // string objects. This logic would typically be augmented with guard objects // that call destructors in case of an exception. //.. // void manipulateStrings(const my_String *stringArray, int len) // { // assert(len <= 10); // // bsls::ObjectBuffer<my_String> tempArray[10]; // for (int i = 0; i < len; ++i) { // new (tempArray[i].buffer()) my_String(stringArray[i]); // assert(stringArray[i] == tempArray[i].object()) // } // // for (int i = 0; i < len; ++i) // { // my_String& s = tempArray[i].object(); // // ... String manipulations go here. 's' might be analyzed, // // appended-to, passed to other functions, etc. // } // // while (len) { // // Destroy strings. Although not critical to this example, we // // follow the general rule of destroying the objects in reverse // // order of their construction, thus mimicking the // // compiler-generated destruction order for normal array objects. // tempArray[--len].object().~my_String(); // } // } // // int main() // { // const my_String INARRAY[3] = { // my_String("hello"), // my_String("goodbye"), // my_String("Bloomberg") // }; // // manipulateStrings(INARRAY, 3); // // return 0; // } //.. // ///Example 2: Containing Different Object Types /// - - - - - - - - - - - - - - - - - - - - - - // Here we use 'bsls::ObjectBuffer' to compose a variable-type object capable // of holding a string or an integer: //.. // class my_Union { // public: // enum TypeTag { INT, STRING }; // // private: // TypeTag d_type; // union { // int d_int; // bsls::ObjectBuffer<my_String> d_string; // }; // // public: // my_Union(int i = 0) : d_type(INT) { d_int = i; } // IMPLICIT // my_Union(const my_String& s) : d_type(STRING) { // IMPLICIT // new (d_string.buffer()) my_String(s); } // my_Union(const char *s) : d_type(STRING) { // IMPLICIT // new (d_string.buffer()) my_String(s); } // my_Union(const my_Union& rhs) : d_type(rhs.d_type) { // if (INT == d_type) { // d_int = rhs.d_int; // } // else { // new (d_string.buffer()) my_String(rhs.d_string.object()); // } // } // ~my_Union() { // if (STRING == d_type) d_string.object().~my_String(); } // // my_Union& operator=(const my_Union& rhs) { // if (INT == d_type) { // if (INT == rhs.d_type) { // d_int = rhs.d_int; // } // else { // if STRING == rhs.d_type // new (d_string.buffer()) my_String(rhs.d_string.object()); // } // } // else { // if (STRING == d_type) // if (INT == rhs.d_type) { // d_string.object().~my_String(); // d_int = rhs.d_int; // } // else { // if STRING == rhs.d_type // d_string.object() = rhs.d_string.object(); // } // } // d_type = rhs.d_type; // return *this; // } // // TypeTag typeTag() const { return d_type; } // // int asInt() const { // return INT == d_type ? // d_int : static_cast<int>( // strtol(d_string.object().c_str(), 0, 0)); // } // // my_String asString() const { // if (INT == d_type) { // char temp[15]; // sprintf(temp, "%d", d_int); // return my_String(temp); // } // else { // return d_string.object(); // } // } // }; // // int main() // { // assert(sizeof(bsls::ObjectBuffer<my_String>) == sizeof(my_String)); // // // Create a 'my_Union' object containing a string. // const my_Union U1("hello"); // assert(my_Union::STRING == U1.typeTag()); // assert(0 == U1.asInt()); // assert("hello" == U1.asString()); // // // Create a 'my_Union' object containing an integer. // const my_Union U2(123); // assert(my_Union::INT == U2.typeTag()); // assert(123 == U2.asInt()); // assert("123" == U2.asString()); // // // Create a 'my_Union' object containing a string that can be // // interpreted as an integer. // const my_Union U3("0x456"); // assert(my_Union::STRING == U3.typeTag()); // assert(0x456 == U3.asInt()); // assert("0x456" == U3.asString()); // // // Copy-construct a 'my_Union' object containing a string. // my_Union u4(U3); // assert(my_Union::STRING == u4.typeTag()); // assert(0x456 == u4.asInt()); // assert("0x456" == u4.asString()); // // // Use assignment to change 'u4' from string to integer. // u4 = U2; // assert(my_Union::INT == u4.typeTag()); // assert(123 == u4.asInt()); // assert("123" == u4.asString()); // // return 0; // } //.. #include <bsls_alignmentfromtype.h> namespace BloombergLP { namespace bsls { // ================== // union ObjectBuffer // ================== template <class TYPE> union ObjectBuffer { // An instance of this union is a raw block of memory suitable for storing // an object of type 'TYPE'. Specifically, the size and alignment of this // union exactly matches that of 'TYPE'. A 'TYPE' object can be // constructed into an 'ObjectBuffer' using the placement 'new' operator // and can be destroyed by explicitly calling its destructor, '~TYPE()'. // It is the user's responsibility to perform this construction and // destruction; an 'ObjectBuffer' object does not manage the construction // or destruction of any other objects. // // Note that a collaboration is implied between 'ObjectBuffer' and the // user. An 'ObjectBuffer' provides aligned memory and the user handles // construction and destruction of the object contained within that memory. private: // DATA // Buffer correctly sized and aligned for object of type 'TYPE'. char d_buffer[sizeof(TYPE)]; typename AlignmentFromType<TYPE>::Type d_align; public: // CREATORS // Note that we deliberately omit defining constructors and destructors in // order to keep this union "POD-like". In particular, an 'ObjectBuffer' // may be used as a member in another 'union'. Copying an 'ObjectBuffer' // by assignment or copy construction will result in a bit-wise copy and // will not invoke 'TYPE's assignment operator or copy constructor. // MANIPULATORS TYPE *address(); // Return the address of the first byte of this object, cast to a // 'TYPE *' pointer. char *buffer(); // Return the address of the first byte of this object, cast to a // 'char *' pointer. TYPE& object(); // Return a modifiable reference to the 'TYPE' object occupying this // buffer. The referenced object has undefined state unless a valid // 'TYPE' object has been constructed in this buffer. // ACCESSORS const TYPE *address() const; // Return the address of the first byte of this object, cast to a // 'const TYPE *' pointer. const char *buffer() const; // Return the address of the first byte of this object, cast to a // 'const char *' pointer. const TYPE& object() const; // Return a 'const' reference to the 'TYPE' object occupying this // buffer. The referenced object has undefined state unless a valid // 'TYPE' object has been constructed in this buffer. }; // ============================================================================ // INLINE FUNCTION DEFINITIONS // ============================================================================ // ------------------ // union ObjectBuffer // ------------------ // MANIPULATORS template <class TYPE> inline TYPE *ObjectBuffer<TYPE>::address() { // A redundant cast to 'void *' persuades gcc/Solaris that there are no // alignment issues to warn about. return reinterpret_cast<TYPE *>(static_cast<void *>(d_buffer)); } template <class TYPE> inline char *ObjectBuffer<TYPE>::buffer() { return d_buffer; } template <class TYPE> inline TYPE& ObjectBuffer<TYPE>::object() { return *reinterpret_cast<TYPE *>(this); } // ACCESSORS template <class TYPE> inline const TYPE *ObjectBuffer<TYPE>::address() const { // A redundant cast to 'const void *' persuades gcc/Solaris that there are // no alignment issues to warn about. return reinterpret_cast<const TYPE *>(static_cast<const void *>(d_buffer)); } template <class TYPE> inline const char *ObjectBuffer<TYPE>::buffer() const { return d_buffer; } template <class TYPE> inline const TYPE& ObjectBuffer<TYPE>::object() const { return *reinterpret_cast<const TYPE *>(this); } } // close package namespace // ============================================================================ // BACKWARD COMPATIBILITY // ============================================================================ #ifndef BDE_OMIT_INTERNAL_DEPRECATED #ifdef bdes_ObjectBuffer #undef bdes_ObjectBuffer #endif #define bdes_ObjectBuffer bsls::ObjectBuffer // This alias is defined for backward compatibility. #endif // BDE_OMIT_INTERNAL_DEPRECATED #ifndef BDE_OPENSOURCE_PUBLICATION // BACKWARD_COMPATIBILITY #ifdef bsls_ObjectBuffer #undef bsls_ObjectBuffer #endif #define bsls_ObjectBuffer bsls::ObjectBuffer // This alias is defined for backward compatibility. #endif // BDE_OPENSOURCE_PUBLICATION -- BACKWARD_COMPATIBILITY } // close enterprise namespace #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 ----------------------------------