Provide a namespace defining nullable value functions.
More...
Detailed Description
- Outline
-
-
- Purpose:
- Provide a namespace defining nullable value functions.
-
- Classes:
-
- See also:
- Component bdlb_nullablevalue
-
- Description:
- The
bdlat_NullableValueFunctions
namespace
provided in this component defines parameterized functions that expose "nullable" behavior for "nullable" types. See the bdlat
package-level documentation for a full description of "nullable" types.
- The functions in this namespace allow users to:
-
make the nullable object contain a value (
makeValue
),
-
manipulate the value contained in a nullable object using a parameterized manipulator functor (
manipulateValue
),
-
access the value contained in a nullable object using a parameterized accessor functor (
accessValue
), and
-
check whether the nullable object is null or not (
isNull
).
- A type becomes part of the
bdlat
"nullable" framework by creating, in the namespace where the type is defined, specializations of the following four (free) function templates:
- A type becomes part of the
bdlat
"nullable" 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 "nullable" type must also define two meta-functions in the bdlat_NullableValueFunctions
namespace:
-
the meta-function
IsNullableValue
contains a compile-time constant VALUE
that is non-zero if the parameterized TYPE
exposes "nullable" behavior, and
-
the
ValueType
meta-function contains a typedef
Type
that specifies the type of the value that can be stored in the parameterized "nullable" type.
- Note that
bdlb::NullableValue<TYPE>
is already part of the bldat
infrastructure for "nullable" 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 a "Nullable" Type:
- - - - - - - - - - - - - - - - - - - Suppose you had a type whose value could be in a "null" state.
namespace BloombergLP {
namespace mine {
struct MyNullableValue {
bool d_isNull;
int d_value;
MyNullableValue()
{
d_isNull = true;
}
};
}
}
We can now make mine::MyNullableValue
expose "nullable" behavior by implementing the necessary bdlta_NullableValueFunctions
for MyNullableValue
inside the mine
namespace and defining the required meta-functions withing the bdlat_NullableValueFunctions
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. Finally, we specialize the IsNullableValue
and ValueType
meta-functions in the bdlat_NullableValueFunctions
namespace for the mine::MyNullableValue
type: namespace BloombergLP {
namespace bdlat_NullableValueFunctions {
template <>
struct IsNullableValue<mine::MyNullableValue> {
enum { VALUE = 1 };
};
template <>
struct ValueType<mine::MyNullableValue> {
typedef int Type;
};
}
}
This completes the bdlat
infrastructure for mine::MyNullableValue
and allows the generic software to recognize the type as a nullable abstraction.
-
- Example 2: Using the Infrastructure Via General Methods:
- The
bdlat
"nullable" framework provides a set of fundamental operations common to any "nullable" 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::MyNullableValue
, which we created in Example 1. For example, we can use the (fundamental) operations in the bdlat_NullableValueFunctions
namespace to operate on mine::NullableValue
, even though they have no knowledge of that type in particular:
- Two of those operations are rather basic. One simply informs whether or not an object is in the null state (the
isNull
method). Another sets an object to a default, non-null state (the makeValue
method). The other two generic methods accomplish their actions via user-supplied functors.
- Let us define a generic functor that gives us access to the underlying value of the "nullable" type, if it's not null:
template <class VALUE_TYPE>
class GetValueAccessor {
VALUE_TYPE *d_value_p;
public:
explicit GetValueAccessor(VALUE_TYPE *value)
: d_value_p(value)
{
}
int operator()(const VALUE_TYPE& containedValue)
{
*d_value_p = containedValue;
return 0;
}
};
Notice that the above class makes no assumptions about the value being accessed other than it can be copied (in the constructor) and assigned (in the operator).
- This functor can be used to fetch the value of our nullable object: Notice that we did not invoke
accessValue
until object
had been set to a non-null state. Doing otherwise would have led to undefined behavior.
- Finally, let's define a functor to set the state of a nullable object:
template <class VALUE_TYPE>
class SetValueManipulator {
VALUE_TYPE d_value;
public:
explicit SetValueManipulator(const VALUE_TYPE& value)
: d_value(value)
{
}
int operator()(VALUE_TYPE *value) const
{
*value = d_value;
return 0;
}
};
As with the previous functor, this functor has no knowledge of the nullable type to which it will be applied. The only assumption here is that the value (type) of our nullable type can be copy constructed and copy assigned.
- Let us use this functor to modify one of our nullable objects:
-
- 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 value associated with an arbitrary "nullable" type.
- These functors make minimal assumptions of
VALUE_TYPE
, merely that it is copy constructable and copy assignable.
struct NullableValueUtil {
template <class NULLABLE_VALUE_TYPE>
static int getValue(
typename bdlat_NullableValueFunctions
::ValueType<NULLABLE_VALUE_TYPE>::Type *value,
const NULLABLE_VALUE_TYPE& object)
{
BSLMF_ASSERT(bdlat_NullableValueFunctions
::IsNullableValue<NULLABLE_VALUE_TYPE>::VALUE);
BSLS_ASSERT(!bdlat_NullableValueFunctions::isNull(object));
typedef typename bdlat_NullableValueFunctions
::ValueType<NULLABLE_VALUE_TYPE>::Type ValueType;
GetValueAccessor<ValueType> valueAccessor(value);
return bdlat_NullableValueFunctions::accessValue(object,
valueAccessor);
}
template <class NULLABLE_VALUE_TYPE>
static int setValue(NULLABLE_VALUE_TYPE *object,
const typename bdlat_NullableValueFunctions
::ValueType<NULLABLE_VALUE_TYPE>::Type& value)
{
BSLMF_ASSERT(bdlat_NullableValueFunctions
::IsNullableValue<NULLABLE_VALUE_TYPE>::VALUE);
BSLS_ASSERT(object);
BSLS_ASSERT(!bdlat_NullableValueFunctions::isNull(*object));
typedef typename bdlat_NullableValueFunctions
::ValueType<NULLABLE_VALUE_TYPE>::Type ValueType;
SetValueManipulator<ValueType> manipulator(value);
return bdlat_NullableValueFunctions::manipulateValue(object,
manipulator);
}
};
Now, we can use these functors to write generic utility functions for getting and setting the value types of arbitrary "nullable" classes.
-
- Example 4: Achieving Type Independence:
- Finally, suppose we have another type such as
your::YourNullableType
, shown below: namespace BloombergLP {
namespace your {
class YourNullableValue {
bool d_isNull;
bsl::string d_value;
public:
YourNullableValue()
: d_isNull(true)
, d_value()
{
}
void makeValue()
{
d_isNull = false;
d_value.clear();
}
void makeNull()
{
d_isNull = true;
d_value.clear();
}
bsl::string& value()
{
assert(!d_isNull);
return d_value;
}
const bsl::string& value() const
{
assert(!d_isNull);
return d_value;
}
bool isNull() const
{
return d_isNull;
}
};
}
}
Notice that while there are many similarities to mine::MyNullableValue
there are clearly differences:
-
The value type is
bsl::string
, not int
.
-
Attributes are accessed via accessor methods, not public data members.
- Nevertheless, since
your::YourNullableValue
also provides the functions and types expected by the bdlat
infrastructure (not shown) we can successfully use your::YourNullableValue
value instead of mine::MyNullableValue
in the previous usage scenario, with no other changes: Notice that syntax and order of bdlat_NullableValueFunction
functions calls have not been changed. The only difference is that the contained type has changed from int
to bsl::string
.
- Finally, instead of defining a new "nullable" type, we could substitute the existing type template
bdlb::NullableValue
. Note that this component provides specializations of the bdlat_nullableValueFunctions
for that type. Since the accessor and manipulator functions we created earlier are type neutral, we can simply drop bdlb::NullableValue<float>
into our familiar scenario: