BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bdlat_arrayfunctions

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:

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.

// MANIPULATORS
template <class MANIPULATOR>
int bdlat_arrayManipulateElement(YOUR_TYPE *array,
MANIPULATOR& manipulator,
int index);
// Invoke the specified 'manipulator' on the address of the element at
// the specified 'index' of the specified 'array'. Return the value
// from the invocation of 'manipulator'. The behavior is undefined
// unless '0 <= index' and 'index < bdlat_arraySize(*array)'.
void resize(YOUR_TYPE *array, int newSize);
// Set the size of the specified modifiable 'array' to the specified
// 'newSize'. If 'newSize > bdlat_arraySize(*array)', then
// 'newSize - bdlat_arraySize(*array)' elements with default values
// (i.e., 'ElementType()') are appended to 'array'. If
// 'newSize < bdlat_arraySize(*array)', then the
// 'bdlat_arraySize(*array) - newSize' elements at the end of 'array'
// are destroyed. The behavior is undefined unless '0 <= newSize'.
// ACCESSORS
template <class ACCESSOR>
int bdlat_arrayAccessElement(const YOUR_TYPE& array,
ACCESSOR& accessor,
int index);
// Invoke the specified 'accessor' on a 'const'-reference to the
// element at the specified 'index' of the specified 'array'. Return
// the value from the invocation of 'accessor'. The behavior is
// undefined unless '0 <= index' and 'index < bdlat_arraySize(array)'.
bsl::size_t bdlat_arraySize(const YOUR_TYPE& array);
// Return the number of elements in the specified 'array'.

The "array" type must also define two meta-functions in the bdlat_ArrayFunctions namespace:

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

This section illustrates intended use 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:
// CREATORS
MyIntArray()
: d_data_p(0)
, d_size(0)
{
}
~MyIntArray()
{
bsl::free(d_data_p);
}
// MANIPULATORS
void resize(bsl::size_t newSize);
int& value(bsl::size_t index)
{
assert(index < d_size);
return d_data_p[index];
}
// ACCESSORS
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)
{
// Always match buffer to size exactly.
if (d_size == newSize) {
return; // 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;
}
} // close namespace mine
} // close enterprise namespace
void resize(TYPE *array, int newSize)
bsl::size_t size(const TYPE &array)
Return the number of elements in the specified array.

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:

namespace BloombergLP {
namespace mine {
// MANIPULATORS
template <class MANIPULATOR>
int bdlat_arrayManipulateElement(MyIntArray *array,
MANIPULATOR& manipulator,
int index);
// Invoke the specified 'manipulator' on the address of the element at
// the specified 'index' of the specified 'array'. Return the value
// from the invocation of 'manipulator'. The behavior is undefined
// unless '0 <= index' and 'index < bdlat_arraySize(*array)'.
void bdlat_arrayResize(MyIntArray *array, int newSize);
// Set the size of the specified modifiable 'array' to the specified
// 'newSize'. If 'newSize > bdlat_arraySize(*array)', then
// 'newSize - bdlat_arraySize(*array)' elements with default values
// (i.e., 'ElementType()') are appended to 'array'. If
// 'newSize < bdlat_arraySize(*array)', then the
// 'bdlat_arraySize(*array) - newSize' elements at the end of 'array'
// are destroyed. The behavior is undefined unless '0 <= newSize'.
// ACCESSORS
template <class ACCESSOR>
int bdlat_arrayAccessElement(const MyIntArray& array,
ACCESSOR& accessor,
int index);
// Invoke the specified 'accessor' on a 'const'-reference to the
// element at the specified 'index' of the specified 'array'. Return
// the value from the invocation of 'accessor'. The behavior is
// undefined unless '0 <= index' and 'index < bdlat_arraySize(array)'.
bsl::size_t bdlat_arraySize(const MyIntArray& array);
// Return the number of elements in the specified 'array'.
} // close namespace mine
} // close enterprise 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 {
// MANIPULATORS
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);
}
// ACCESSORS
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();
}
} // close namespace mine
} // close enterprise namespace
int bdlat_arrayAccessElement(const bsl::vector< TYPE, ALLOC > &array, ACCESSOR &accessor, int index)
bsl::size_t bdlat_arraySize(const bsl::vector< TYPE, ALLOC > &array)

Finally, we specialize the IsArray and ElementType meta-functions in the bdlat_ArrayFunctions namespace for the mine::MyIntArray type:

namespace BloombergLP {
// TRAITS
template <>
struct IsArray<mine::MyIntArray> : bsl::true_type {
};
template <>
struct ElementType<mine::MyIntArray> {
typedef int Type;
};
} // close namespace bdlat_ArrayFunctions
} // close enterprise namespace
Definition bdlat_arrayfunctions.h:702

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:

void usageMakeArray()
{
mine::MyIntArray array;
assert(0 == bdlat_ArrayFunctions::size(array));
assert(8 == bdlat_ArrayFunctions::size(array));
assert(4 == bdlat_ArrayFunctions::size(array));
}
#define BSLMF_ASSERT(expr)
Definition bslmf_assert.h:229
Definition bdlat_arrayfunctions.h:718

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 {
// DATA
ELEMENT_TYPE *d_element_p;
public:
// CREATORS
explicit GetElementAccessor(ELEMENT_TYPE *value)
: d_element_p(value)
{
}
// MANIPULATORS
int operator()(const ELEMENT_TYPE& elementValue)
{
*d_element_p = elementValue;
return 0;
}
};
template<class ELEMENT_TYPE>
class SetElementManipulator {
// DATA
ELEMENT_TYPE d_value;
public:
// CREATORS
SetElementManipulator(const ELEMENT_TYPE& value)
: d_value(value)
{
}
// ACCESSOR
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:

void usageArrayElements()
{
mine::MyIntArray array;
// Confirm initial array elements from resize.
int value;
GetElementAccessor<int> accessor(&value);
for (int index = 0; index < 4; ++index) {
accessor,
index);
assert(0 == rc);
assert(0 == value)
}
// Set element 'index * 10' as its value;
for (int index = 0; index < 4; ++index) {
SetElementManipulator<int> manipulator(index * 10);
manipulator,
index);
assert(0 == rc);
}
// Confirm new value of each element.
for (int index = 0; index < 4; ++index) {
accessor,
index);
assert(0 == rc);
assert(index * 10 == value);
}
}
int manipulateElement(TYPE *array, MANIPULATOR &manipulator, int index)
int accessElement(const TYPE &array, ACCESSOR &accessor, int index)

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:

struct ArrayUtil {
// CLASS METHODS
template <class ARRAY_TYPE>
static int getElement(typename bdlat_ArrayFunctions
::ElementType<ARRAY_TYPE>::Type *value,
const ARRAY_TYPE& object,
int index)
// Load to the specified 'value' the element at the specified
// 'index' of the specified 'object' array. Return 0 if the
// element is successfully loaded to 'value', and a non-zero value
// otherwise. This function template requires that the specified
// 'ARRAY_TYPE' is a 'bdlat' "array" type. The behavior is
// undefined unless '0 <= index' and
// 'index < bdlat_ArrayFunctions::size(object)'.
{
typedef typename bdlat_ArrayFunctions
::ElementType<ARRAY_TYPE>::Type ElementType;
GetElementAccessor<ElementType> elementAccessor(value);
elementAccessor,
index);
}
template <class ARRAY_TYPE>
static int setElement(
ARRAY_TYPE *object,
int index,
::Type& value)
// Assign the specified 'value' to the element of the specified
// 'object' array at the specified 'index'. Return 0 if the
// element is successfully assigned to 'value', and a non-zero
// value otherwise. This function template requires that the
// specified 'ARRAY_TYPE' is a 'bdlat' "array" type. The behavior
// is undefined unless '0 <= index' and
// 'index < bdlat_ArrayFunctions::size(*object)'.
{
::Type ElementType;
SetElementManipulator<ElementType> manipulator(value);
manipulator,
index);
}
};
Definition bdlat_arrayfunctions.h:712

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;
// Confirm initial values.
for (int index = 0; index < 4; ++index) {
int value;
int rc = ArrayUtil::getElement(&value, array, index);
assert(0 == rc);
assert(0 == value);
}
// Set element 'index * 10' as its value;
for (int index = 0; index < 4; ++index) {
int value = index * 10;
int rc = ArrayUtil::setElement(&array, index, value);
assert(0 == rc);
}
// Confirm value of each element.
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:
// CREATORS
MyFloatArray()
: d_data_p(0)
, d_size(0)
{
}
~MyFloatArray()
{
delete[] d_data_p;
}
// MANIPULATORS
void setSize(bsl::size_t newSize); // Too large for inline.
float& element(bsl::size_t index)
{
assert(index < d_size);
return d_data_p[index];
}
// ACCESSORS
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:

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;
// Confirm initial values.
for (int index = 0; index < 4; ++index) {
float value;
int rc = ArrayUtil::getElement(&value, array, index);
assert(0 == rc);
assert(0.0 == value);
}
// Set element 'index * 10' as its 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);
}
// Confirm value of each element.
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; // STANDARD ARRAY TYPE
// Confirm initial values.
for (int index = 0; index < 4; ++index) {
bsl::string value;
int rc = ArrayUtil::getElement(&value, array, index);
assert(0 == rc);
assert("" == value);
}
// Set element 'index * 10' as its 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);
}
// Confirm value of each element.
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);
}
}
Definition bslstl_ostringstream.h:175
void str(const StringType &value)
Definition bslstl_ostringstream.h:581
Definition bslstl_string.h:1281
Definition bslstl_vector.h:1025