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:
-
- 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();
}
while (len) {
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; }
my_Union(const my_String& s) : d_type(STRING) {
new (d_string.buffer()) my_String(s); }
my_Union(const char *s) : d_type(STRING) {
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 {
new (d_string.buffer()) my_String(rhs.d_string.object());
}
}
else {
if (INT == rhs.d_type) {
d_string.object().~my_String();
d_int = rhs.d_int;
}
else {
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));
const my_Union U1("hello");
assert(my_Union::STRING == U1.typeTag());
assert(0 == U1.asInt());
assert("hello" == U1.asString());
const my_Union U2(123);
assert(my_Union::INT == U2.typeTag());
assert(123 == U2.asInt());
assert("123" == U2.asString());
const my_Union U3("0x456");
assert(my_Union::STRING == U3.typeTag());
assert(0x456 == U3.asInt());
assert("0x456" == U3.asString());
my_Union u4(U3);
assert(my_Union::STRING == u4.typeTag());
assert(0x456 == u4.asInt());
assert("0x456" == u4.asString());
u4 = U2;
assert(my_Union::INT == u4.typeTag());
assert(123 == u4.asInt());
assert("123" == u4.asString());
return 0;
}