Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bsls_objectbuffer
[Package bsls]

Provide raw buffer with size and alignment of user-specified type. More...

Namespaces

namespace  bsls

Detailed Description

Outline
Purpose:
Provide raw buffer with size and alignment of user-specified type.
Classes:
bsls::ObjectBuffer parameterized buffer aligned to hold specified type
See also:
Component 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;
  }