8.1 Containing Record Macro
The containing record macro, called CR()
, enables good object-oriented
programming practices. It returns a pointer to the structure using a pointer to
one of the structure's fields. Protocol producing UEFI drivers use this macro
to retrieve the private context data structure from a pointer to a produced
protocol interface. Protocol functions are required to pass in a pointer to the
protocol instance as the first argument to the function. C++ does this automatically, and the pointer to the object instance is called a this pointer. Since UEFI drivers are written in C, a close
equivalent is implemented by requiring that the first argument of every
protocol function be the pointer to the protocol's instance structure called
"This." Each protocol function then uses the CR()
macro to retrieve a pointer
to the private context data structure from this first argument called This.
The example below is the definition of the CR()
macro from the EDK II library
DebugLib
. The CR()
macro is provided a pointer to the following:
A field in a data structure
The name of the field
It uses this information to compute the offset of the field in the data
structure and subtracts this offset from the pointer to the field. This
calculation results in a pointer to the data structure that contains the
specified field. BASE_CR()
returns a pointer to the data structure that
contains the specified field. For debug builds, CR()
also does an additional
check to verify a signature value. If the signature value does not match, an
ASSERT()
message is generated and the system is halted or generates a
breakpoint. For production builds, the signature checks are typically disabled.
Most UEFI drivers define additional macros based on the CR()
macro that
retrieves the private context data structure based on a This pointer to a
produced protocol. These additional macros are typically given names that make
it easier to understand in the source code that the This pointer is being
used to retrieve the private context data structure defined by the UEFI Driver.
Example 111-Containing record macro definitions
/**
Macro that calls DebugAssert() if the containing record does not have a matching signature. If the signatures matches, then a pointer to the data structure that contains a specified field of that data structure is returned. This is a lightweight method that hides information by placing a public data structure inside a larger private data structure and using a pointer to the public data structure to retrieve a pointer to the private data structure.
If the data type specified by TYPE does not contain the field specified by Field, then the module will not compile.
If TYPE does not contain a field called Signature, then the module will not compile.
@param Record The pointer to the field specified by Field within a data structure of type TYPE.
@param TYPE The name of the data structure type to return
This data structure must contain the field specified by Field.
@param Field The name of the field in the data structure specified by TYPE to which Record points.
@param TestSignature The 32-bit signature value to match.
**/
#if !defined(MDEPKG_NDEBUG)
#define CR(Record, TYPE, Field, TestSignature) \
(DebugAssertEnabled () && \
(BASE_CR (Record, TYPE, Field)->Signature != TestSignature)) ? \
(TYPE *)(_ASSERT (CR has Bad Signature), Record) : \
BASE_CR (Record, TYPE, Field)
#else
#define CR(Record, TYPE, Field, TestSignature) \
BASE_CR (Record, TYPE, Field)
#endif
The following example shows the definition of the BASE_CR()
macro from the
EDK II that is used to implement the CR()
macro above. The BASE_CR()
macro
does not perform any signature checking or handle any error conditions. This
macro may be used with data structures that do not have a Signature field.
Example 112-Containing record macro definitions
/**
Macro that returns a pointer to the data structure that contains a specified field of that data structure. This is a lightweight method to hide information by placing a public data structure inside a larger private data structure and using a pointer to the public data structure to retrieve a pointer to the private data structure.
This function computes the offset, in bytes, of field specified by Field from the beginning of the data structure specified by TYPE. This offset is subtracted from Record, and is used to return a pointer to a data structure of the type specified by TYPE. If the data type specified by TYPE does not contain the field specified by Field, then the module will not compile.
@param Record Pointer to the field specified by Field within a
data structure of type TYPE.
@param TYPE The name of the data structure type to return. This
data structure must contain the field specified by
Field.
@param Field The name of the field in the data structure specified
by TYPE to which Record points.
@return A pointer to the structure from one of it's elements.
**/
#define BASE_CR(Record, TYPE, Field) \
((TYPE *)((CHAR8 *)(Record) - (CHAR8 *) &(((TYPE *) 0)->Field)))