Provide a namespace defining "array" functions.
More...
Detailed Description
- Outline
-
-
- Purpose:
- Provide a namespace defining "array" functions.
-
- Classes:
-
-
- Description:
- The
bdlat_ArrayFunctions
namespace
provided in this component defines parameterized functions that expose "array" behavior for "array" types. See the bdlat
package-level documentation for a full description of "array" types.
- The functions in this namespace allow users to:
-
obtain the number of elements in an array (
size
).
-
set the number of elements in an array (
resize
).
-
manipulate an element in an array using a parameterized manipulator (
manipulateElement
). and
-
access an element in an array using a parameterized accessor (
accessElement
).
- A type becomes part of the
bdlat
"array" framework by creating, in the namespace where the type is defined, overloads of the following two (free) functions and two (free) function templates. Note that the placeholder YOUR_TYPE
is not a template argument and should be replaced with the name of the type being plugged into the framework. The "array" type must also define two meta-functions in the bdlat_ArrayFunctions
namespace:
-
the meta-function
IsArray
contains a compile-time constant VALUE
that is non-zero if the parameterized TYPE
exposes "array" behavior, and
-
the
ElementType
meta-function contains a typedef
Type
that specifies the type of the element stored in the parameterized "array" type.
- Note that
bsl::vector<TYPE>
is already part of the bdlat
infrastructure for "array" types because this component also provides overloads of the required functions and meta-function specializations.
-
- Usage:
- ------ The following code illustrate the usage of this component.
-
- Example 1: Defining an "Array" Type:
- - - - - - - - - - - - - - - - - - Suppose you had a type,
mine::MyIntArray
, that provides the essential features of an "array" type. namespace BloombergLP {
namespace mine {
class MyIntArray {
int *d_data_p;
bsl::size_t d_size;
public:
MyIntArray()
: d_data_p(0)
, d_size(0)
{
}
~MyIntArray()
{
bsl::free(d_data_p);
}
void resize(bsl::size_t newSize);
int& value(bsl::size_t index)
{
assert(index < d_size);
return d_data_p[index];
}
const int& value(bsl::size_t index) const
{
assert(index < d_size);
return d_data_p[index];
}
bsl::size_t size() const
{
return d_size;
}
};
void MyIntArray::resize(bsl::size_t newSize)
{
if (d_size == newSize) {
return;
}
int *newData = static_cast<int *>(bsl::malloc(sizeof(int)
newSize));
if (d_size < newSize) {
bsl::memcpy(newData, d_data_p, d_size * sizeof(int));
std::memset(newData + d_size,
0,
(newSize - d_size) * sizeof(int));
} else {
bsl::memcpy(newData, d_data_p, newSize);
}
bsl::free(d_data_p);
d_data_p = newData;
d_size = newSize;
}
}
}
We can now make mine::MyIntArray
expose "array" behavior by implementing the necessary bdlat_ArrayFunctions
for MyIntArray
inside the mine
namespace and defining the required meta-functions withing the bdlat_ArrayFunctions
namespace.
- First, we should forward declare all the functions that we will implement inside the
mine
namespace: Then, we will implement these functions. Recall that the two (non-template) functions should be defined in some .cpp
file, unless you choose to make them inline
functions. namespace BloombergLP {
namespace mine {
template <class MANIPULATOR>
int bdlat_arrayManipulateElement(MyIntArray *array,
MANIPULATOR& manipulator,
int index)
{
assert(array);
assert(0 <= index);
assert(static_cast<bsl::size_t>(index) < array->size());
return manipulator(&array->value(index));
}
void bdlat_arrayResize(MyIntArray *array, int newSize)
{
assert(array);
assert(0 <= newSize);
array->resize(newSize);
}
template <class ACCESSOR>
int bdlat_arrayAccessElement(const MyIntArray& array,
ACCESSOR& accessor,
int index)
{
assert(0 <= index);
assert(static_cast<bsl::size_t>(index) < array.size());
return accessor(array.value(index));
}
bsl::size_t bdlat_arraySize(const MyIntArray& array)
{
return array.size();
}
}
}
Finally, we specialize the IsArray
and ElementType
meta-functions in the bdlat_ArrayFunctions
namespace for the mine::MyIntArray
type: namespace BloombergLP {
namespace bdlat_ArrayFunctions {
template <>
struct IsArray<mine::MyIntArray> {
enum { VALUE = 1 };
};
template <>
struct ElementType<mine::MyIntArray> {
typedef int Type;
};
}
}
This completes the bdlat
infrastructure for mine::MyIntArray
and allows the generic software to recognize the type as an array abstraction.
-
- Example 2: Using the Infrastructure Via General Methods:
- The
bdlat
"array" framework provides a set of fundamental operations common to any "array" type. We can build upon these operations to make our own utilities, or use them on our own types that are plugged into the framework, like mine::MyIntArray
, which we created in Example 1. For example, we can use the (fundamental) operations in the bdlat_ArrayFunctions
namespace to operate on mine::MyIntArray
, even though they have no knowledge of that type in particular: To perform operations on the elements of an array requires use of the functions that employ accessor and manipulator functors. For example: template <class ELEMENT_TYPE>
class GetElementAccessor {
ELEMENT_TYPE *d_element_p;
public:
explicit GetElementAccessor(ELEMENT_TYPE *value)
: d_element_p(value)
{
}
int operator()(const ELEMENT_TYPE& elementValue)
{
*d_element_p = elementValue;
return 0;
}
};
template<class ELEMENT_TYPE>
class SetElementManipulator {
ELEMENT_TYPE d_value;
public:
SetElementManipulator(const ELEMENT_TYPE& value)
: d_value(value)
{
}
int operator()(ELEMENT_TYPE *element) const
{
*element = d_value;
return 0;
}
};
Notice that these functors make few assumptions of ELEMENT_TYPE
, merely that it is copy constructable and copy assignable.
- With these definitions we can now use the generic functions to set and get values from an
mine::MyIntArray
object:
-
- Example 3: Defining Utility Functions:
- Creating functor objects for each operation can be tedious and error prone; consequently, those types are often executed via utility functions.
- Suppose we want to create utilities for getting and setting the elements of an arbitrary "array" type. We might define a utility
struct
, ArrayUtil
, a namespace for those functions: Now, we can use these functors to write generic utility functions for getting and setting the value types of arbitrary "array" classes. void myUsageScenario()
{
mine::MyIntArray array;
bdlat_ArrayFunctions::resize(&array, 4);
for (int index = 0; index < 4; ++index) {
int value;
int rc = ArrayUtil::getElement(&value, array, index);
assert(0 == rc);
assert(0 == value);
}
for (int index = 0; index < 4; ++index) {
int value = index * 10;
int rc = ArrayUtil::setElement(&array, index, value);
assert(0 == rc);
}
for (int index = 0; index < 4; ++index) {
int value;
int rc = ArrayUtil::getElement(&value, array, index);
assert(0 == rc);
assert(index * 10 == value);
}
}
-
- Example 4: Achieving Type Independence:
- Suppose we have another type such as
your::YourFloatArray
, shown below: namespace BloombergLP {
namespace your {
class MyFloatArray {
float *d_data_p;
bsl::size_t d_size;
bsl::size_t d_capacity;
public:
MyFloatArray()
: d_data_p(0)
, d_size(0)
{
}
~MyFloatArray()
{
delete[] d_data_p;
}
void setSize(bsl::size_t newSize);
float& element(bsl::size_t index)
{
assert(index < d_size);
return d_data_p[index];
}
const float& element(bsl::size_t index) const
{
assert(index < d_size);
return d_data_p[index];
}
bsl::size_t numElements() const
{
return d_size;
}
bsl::size_t capacity() const
{
return d_capacity;
}
};
Notice that while there are many similarities to mine::MyIntArray
, there are also significant differences:
-
The element type is
float
, not int
.
-
Many of the accessors are named differently (e.g.,
numElements
instead of size
, setSize
instead of resize
).
-
There is an additional attribute,
capacity
, because this class has a setSize
method (not shown) that reduces calls to the heap by over allocating when the size is increased beyond the current capacity.
- Nevertheless, since
your::YourFloatArray
also provides the functions and types expected by the bdlat
infrastructure (not shown) we can successfully use your::FloatArray
value instead of mine::MyIntArray
in the previous usage scenario, with no other changes: void yourUsageScenario()
{
your::YourFloatArray array;
bdlat_ArrayFunctions::resize(&array, 4);
for (int index = 0; index < 4; ++index) {
float value;
int rc = ArrayUtil::getElement(&value, array, index);
assert(0 == rc);
assert(0.0 == value);
}
for (int index = 0; index < 4; ++index) {
float value = static_cast<float>(index * 10);
int rc = ArrayUtil::setElement(&array, index, value);
assert(0 == rc);
}
for (int index = 0; index < 4; ++index) {
float value;
int rc = ArrayUtil::getElement(&value, array, index);
assert(0 == rc);
assert(static_cast<float>(index * 10) == value);
}
}
Notice that syntax and order of bdlat_ArrayFunctions
function calls have not been changed. The only difference is that the element type has changed from int
to float
.
- Finally, instead of defining a new "array" type, we could substitute the existing type template
bsl::vector
. Note that this component provides specializations of the bdlat_ArrayFunctions
for that type. Since the accessor and manipulator functions we created earlier are type neutral, we can simply drop bsl::vector<bsl::string>
into our familiar scenario: void anotherUsageScenario()
{
bsl::vector<bsl::string> array;
bdlat_ArrayFunctions::resize(&array, 4);
for (int index = 0; index < 4; ++index) {
bsl::string value;
int rc = ArrayUtil::getElement(&value, array, index);
assert(0 == rc);
assert("" == value);
}
for (int index = 0; index < 4; ++index) {
bsl::ostringstream oss; oss << (index * 10);
int rc = ArrayUtil::setElement(&array, index, oss.str());
assert(0 == rc);
}
for (int index = 0; index < 4; ++index) {
bsl::string value;
int rc = ArrayUtil::getElement(&value, array, index);
bsl::ostringstream oss; oss << (index * 10);
assert(0 == rc);
assert(oss.str() == value);
}
}