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.
template <class MANIPULATOR>
int bdlat_arrayManipulateElement(YOUR_TYPE *array,
MANIPULATOR& manipulator,
int index);
void resize(YOUR_TYPE *array, int newSize);
template <class ACCESSOR>
int bdlat_arrayAccessElement(const YOUR_TYPE& array,
ACCESSOR& accessor,
int index);
bsl::size_t bdlat_arraySize(const YOUR_TYPE& array);
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
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:
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];
}
{
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;
}
}
}
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 {
template <class MANIPULATOR>
int bdlat_arrayManipulateElement(MyIntArray *array,
MANIPULATOR& manipulator,
int index);
void bdlat_arrayResize(MyIntArray *array, int newSize);
template <class ACCESSOR>
int bdlat_arrayAccessElement(const MyIntArray& array,
ACCESSOR& accessor,
int index);
bsl::size_t bdlat_arraySize(const MyIntArray& array);
}
}
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>
ACCESSOR& accessor,
int index)
{
assert(0 <= index);
assert(static_cast<bsl::size_t>(index) < array.size());
return accessor(array.value(index));
}
{
return array.size();
}
}
}
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 {
template <>
};
template <>
struct ElementType<mine::MyIntArray> {
typedef int Type;
};
}
}
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;
}
#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 {
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:
void usageArrayElements()
{
mine::MyIntArray array;
int value;
GetElementAccessor<int> accessor(&value);
for (int index = 0; index < 4; ++index) {
accessor,
index);
assert(0 == rc);
assert(0 == value)
}
for (int index = 0; index < 4; ++index) {
SetElementManipulator<int> manipulator(index * 10);
manipulator,
index);
assert(0 == rc);
}
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 {
template <class ARRAY_TYPE>
::ElementType<ARRAY_TYPE>::Type *value,
const ARRAY_TYPE& object,
int index)
{
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)
{
::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;
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;
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()
{
for (int index = 0; index < 4; ++index) {
int rc = ArrayUtil::getElement(&value, array, index);
assert(0 == rc);
assert("" == value);
}
for (int index = 0; index < 4; ++index) {
int rc = ArrayUtil::setElement(&array, index, oss.
str());
assert(0 == rc);
}
for (int index = 0; index < 4; ++index) {
int rc = ArrayUtil::getElement(&value, array, index);
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