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)))