Provide a meta-function that maps a TYPE
to its alignment.
More...
Detailed Description
- Outline
-
-
- Purpose:
- Provide a meta-function that maps a
TYPE
to its alignment.
-
- Classes:
-
- See also:
- Component bsls_alignmenttotype
-
- Description:
- This component contains a template meta-function,
bsls::AlignmentFromType
, parameterized on TYPE
, that defines an integral constant VALUE
initialized (at compile-time) to the required alignment for TYPE
. bsls::AlignmentFromType
also provides a typedef
, Type
, that is an alias for a primitive type that has the same alignment requirements as TYPE
.
-
- Terminology:
- Efficient Alignment is any alignment that prevents the CPU from performing unaligned memory access.
- Compiler Alignment is the alignment chosen for a data type by a specific compiler with a specific set of compile-time options. On most platforms, the compiler can be instructed to pack all structures with 1-byte alignment, even if inefficient memory access results.
- Required Alignment is synonymous with Compiler Alignment, even when the CPU supports unaligned access. The terms "required alignment" and "alignment requirement" are in common use even though "compiler alignment" is a more precise term.
-
- Surprises and Anomalies:
- Note that efficient alignment for a given type and its size are not identical on all platforms. For example, Linux on 32-bit Intel aligns an 8-byte
double
on a 4-byte boundary within a struct
.
- On platforms with 32-bit words, there is usually no efficiency gain by using more than 4-byte alignment. Yet some compilers use 8-byte alignment for
long long
or double
, presumably so that the code will run faster on a 64-bit CPU.
-
- Usage:
- This section illustrates intended use of this component.
-
- Example 1: Creating a Static "Database" of Types:
- The following shows how
bsls::AlignmentFromType<T>VALUE
can be used to create a static "database" of types storing their size and required alignment.
- This information can be populated into an array of
my_ElemAttr
elements below: enum my_ElemType { MY_CHAR, MY_INT, MY_DOUBLE, MY_POINTER };
struct my_ElemAttr {
my_ElemType d_type;
int d_size;
int d_alignment;
};
static const my_ElemAttr MY_ATTRIBUTES[] = {
{ MY_CHAR, sizeof(char), bsls::AlignmentFromType<char>::VALUE },
{ MY_INT, sizeof(int), bsls::AlignmentFromType<int>::VALUE },
{ MY_DOUBLE, sizeof(double), bsls::AlignmentFromType<double>::VALUE },
{ MY_POINTER, sizeof(void *), bsls::AlignmentFromType<void *>::VALUE }
};
-
- Example 2: Creating an Aligned Buffer:
- Consider a parameterized type,
my_AlignedBuffer
, that provides aligned memory to store a user-defined type. A my_AlignedBuffer
object is useful in situations where efficient (e.g., stack-based) storage is required.
- The
my_AlignedBuffer
union
(defined below) takes a template parameter TYPE
, and provides an appropriately sized and aligned block of memory via the buffer
functions. Note that my_AlignedBuffer
ensures that the returned memory is aligned correctly for the specified size by using bsls::AlignmentFromType<TYPE>Type
, which provides a primitive type having the same alignment requirement as TYPE
. The class definition of my_AlignedBuffer
is as follows: template <class TYPE>
union my_AlignedBuffer {
private:
char d_buffer[sizeof(TYPE)];
typename bsls::AlignmentFromType<TYPE>::Type d_align;
public:
char *buffer();
TYPE& object();
const char *buffer() const;
const TYPE& object() const;
};
The function definitions of my_AlignedBuffer
are as follows:
template <class TYPE>
inline
char *my_AlignedBuffer<TYPE>::buffer()
{
return d_buffer;
}
template <class TYPE>
inline
TYPE& my_AlignedBuffer<TYPE>::object()
{
return *reinterpret_cast<TYPE *>(this);
}
template <class TYPE>
inline
const char *my_AlignedBuffer<TYPE>::buffer() const
{
return d_buffer;
}
template <class TYPE>
inline
const TYPE& my_AlignedBuffer<TYPE>::object() const
{
return *reinterpret_cast<const TYPE *>(this);
}
my_AlignedBuffer
can be used to construct buffers for different types and with varied alignment requirements. Consider that we want to construct an object that stores the response of a floating-point operation. If the operation is successful, then the response object stores a double
result; otherwise, it stores an error string of type string
, which is based on the standard type string
(see bslstl_string
). For the sake of brevity, the implementation of string
is not explored here. Here is the definition for the Response
class: Note that we use my_AlignedBuffer
to allocate sufficient, aligned memory to store the result of the operation or an error message: private:
union {
my_AlignedBuffer<double> d_result;
my_AlignedBuffer<string> d_errorMessage;
};
The isError
flag indicates whether the response object stores valid data or an error message: Below we provide a simple public interface suitable for illustration only: public:
Response(double result);
Response(const string& errorMessage);
~Response();
The manipulator functions allow clients to update the response object to store either a double
result or an error message:
void setResult(double result);
void setErrorMessage(const string& errorMessage);
The isError
function informs clients whether a response object stores a result value or an error message:
bool isError() const;
double result() const;
const string& errorMessage() const;
};
Below we provide the function definitions. Note that we use the my_AlignedBuffer::buffer
function to access correctly aligned memory. Also note that my_AlignedBuffer
just provides the memory for an object; therefore, the Response
class is responsible for the construction and destruction of the specified objects. Since our Response
class is for illustration purposes only, we ignore exception-safety concerns; nor do we supply an allocator to the string constructor, allowing the default allocator to be used instead:
Response::Response(double result)
{
new (d_result.buffer()) double(result);
d_isError = false;
}
Response::Response(const string& errorMessage)
{
new (d_errorMessage.buffer()) string(errorMessage);
d_isError = true;
}
Response::~Response()
{
if (d_isError) {
typedef string Type;
d_errorMessage.object().~Type();
}
}
void Response::setResult(double result)
{
if (!d_isError) {
d_result.object() = result;
}
else {
typedef string Type;
d_errorMessage.object().~Type();
new (d_result.buffer()) double(result);
d_isError = false;
}
}
void Response::setErrorMessage(const string& errorMessage)
{
if (d_isError) {
d_errorMessage.object() = errorMessage;
}
else {
new (d_errorMessage.buffer()) string(errorMessage);
d_isError = true;
}
}
bool Response::isError() const
{
return d_isError;
}
double Response::result() const
{
assert(!d_isError);
return d_result.object();
}
const string& Response::errorMessage() const
{
assert(d_isError);
return d_errorMessage.object();
}
Clients of the Response
class can use it as follows: double value1 = 111.2, value2 = 92.5;
if (0 == value2) {
Response response("Division by 0");
}
else {
Response response(value1 / value2);
}
Define Documentation