adr_data_t
Type
The most frequently used type defined by rad/adr.h
is adr_data_t
. An adr_data_t
object represents a unit of typed data. It could be of a base type, such as an integer ("1") or string ("banana"), or of a derived type like a structure or an array. Each adr_data_t
maintains a pointer to its adr_type_t
.
A few common traits simplify access to adr_data_t
objects. The first is that, except for the structure and array derived types (not enumerations), all adr_data_t
values are immutable. They are assigned a value when they are created, and may not be changed thereafter.
Another is that all adr_data_t
values are reference counted. Sometimes data structures need to be used by multiple consumers simultaneously, or simply retained for subsequent use. Reference counting is a cheap way to cut down on the cost of copying large data structures and the complexity of handling allocation failures. Though the reference counting is thread-safe, there is no other locking, which is not a problem for an immutable adr_data_t
. Though the value of a non-immutable adr_data_t
may be modified post-creation, the convention used throughout rad
and its associated libraries is that once visibility of an adr_data_t
has spread past its creator, it may no longer be modified. This eliminates the need for additional synchronization.
adr_data_t *adr_data_ref(adr_data_t *data); void adr_data_free(adr_data_t *data);
The reference count on the adr_data_t
data is incremented with adr_data_ref
. For convenience, adr_data_ref returns data. Symmetrically, the reference count on the adr_data_t
data is decremented with adr_data_free. As the name implies, this may result in data being freed; after calling adr_data_free the caller must not access data in any way. Neither adr_data_ref nor adr_data_free can fail.
A third trait is that interfaces that accept adr_data_t
values take ownership of the caller's reference on the adr_data_t
. If the caller needs to refer to the adr_data_t
after passing a pointer to it to a libadr
interface, it must first secure an additional reference with adr_data_ref
. Interfaces that return adr_data_t
that are referenced by other adr_data_t
do not increase the reference count on the returned adr_data_t
. The returned value is guaranteed to persist only as long as the caller retains a reference on the referring adr_data_t
, or if the caller uses adr_data_ref
to acquire its own reference on the returned adr_data_t
. The net result is that in the common case where an adr_data_t
does not have multiple simultaneous consumers, libadr
consumers need not perform any explicit reference counting at all. They can naively allocate and free adr_data_t
values as if they were any other data structure. Therefore the adr_data_t
implementation can optimize for the case where the reference count is 1.
Lastly, many adr_data_t
management routines rely on dynamic memory allocation, which means that proper error handling is essential. To increase the clarity and maintainability of adr_data_t
consumers, and reduce the likelihood of mishandling errors, libadr
interfaces explicitly accept NULL adr_data_t
inputs and fail in sympathy. This means that a libadr
consumer can perform a large number of operations on the instances of adr_data_t
, checking only the final result for failure. Additionally, if a libadr
routine is going to fail for any reason, references to a non-NULL adr_data_t
passed to the routine is released. In other words, no special clean-up is needed when a libadr
routine fails.