Blog

Blog Post

do while false pattern
Posted By dan, March 12, 2018

While working on an NDIS Driver for custom hardware, I saw a code pattern that intrigued me. The driver was using a do while false pattern with validated inputs and common error handling. A simplified version is below for reference.

do while false pattern is a single pass do while loop that uses the return code of functions to break out of the loop for efficient flow control.

Why is it interesting? The do while false pattern perfectly implements the separation of concerns, a function should do one thing, and functions should be testable concepts; and, it predates the Gang of Four pattern experts by TEN years. do while false pattern is old school.

Pattern Explained

The procedure is the high level objective. It is an encapsulated sequence of executing functions that has defined input and output containers; kind of like an API. The procedure’s main focus is the workflow of function execution. It is similar to the Service Orientated Architecture concept because it defines an implementation pattern.

The individual functions take in only the parameters they need and perform their one task. Logging the error, cleaning up the resources, and managing the workflow is not its concern. This keeps the functions really clean which is great for reuse and automated testing.

Bonus Naming Convention

The naming convention used here is useful for breaking up code into managable pieces. The procedures are postfixed with procedure, the validators are prefixed with validate, and the functions that perform work are prefixed with perform.

A code review would easily spot “work” being done in the procedure or a function doing too much. Functions that require multiple steps could be refactored into a procedure.

Take Away

The do while false pattern is organized, easy to read, framework free, testable, and can be performed by any language with a do while statement like C, C++, C#, and Java.

Hat Tip, to C code from the 80’s.

Do While False Pattern: Example

The following code is a stripped down and highly modified version of production code in order to show the concept without all the implementation noise.

typedef enum _PROCEDURE_STATUS { 
    PROCEDURE_SUCCESS
    , PROCEDURE_FAILURE
    , PROCEDURE_FAIL_INVALID_HANDLE
    , PROCEDURE_FAIL_INVALID_NETWORK_ID
    , PROCEDURE_FAIL_INVALID_PORT
} PROCEDURE_STATUS;

typedef struct _PROCEDURE_INPUT_CONTAINER {
    BOOLEAN handleReady;
    CHAR networkId;
    uint32_t portId;
    PVOID data;
} PROCEDURE_INPUT_CONTAINER, *PPROCEDURE_INPUT_CONTAINER;

typedef struct _PROCEDUREOUTPUT_CONTAINER {
    BOOLEAN aFlag;
    PVOID data;
} PROCEDURE_OUTPUT_CONTAINER, *PPROCEDURE_OUTPUT_CONTAINER;

PROCEDURE_STATUS
doProcedure(
    _In_ PPROCEDURE_INPUT_CONTAINER pInputContainer
    _Out_ PPROCEDURE_OUTPUT_CONTAINER pOutputContainer
) {
    PROCEDURE_STATUS status = PROCEDURE_SUCCESS;

    do {
        // validate input
        status = validateHandle( pInputContainer->handleReady);
        if (status != PROCEDURE_SUCCESS)
            break;

        status = validateNetworkId( pInputContainer->networkId);
        if (status != PROCEDURE_SUCCESS)
            break;

        status = validatePort( pInputContainer->portId);
        if (status != PROCEDURE_SUCCESS)
            break;

        // do work with validated pInputContainer
        status = performStep1( pInputContainer->pData);
        if (status != PROCEDURE_SUCCESS)
            break;

        status = performStep2( pInputContainer->pData, pOutputContainer);
        if (status != PROCEDURE_SUCCESS)
            break;

        if (pOutputContainer->aFlag == TRUE) {
            status = performStep3a( pInputContainer->pData);
            if (status != PROCEDURE_SUCCESS)
                break;
        } else {
            status = performStep3b( pInputContainer->pData);
            if (status != PROCEDURE_SUCCESS)
                break;
        }

    } while (false);

    if (status != PROCEDURE_SUCCESS) {
        // log error
        // perform clean up
    }

    return status;
}
C++

For C++, I would switch the container structs to classes and pass by reference instead of pointer. Change the functions to static functions where possible.

Contact

Say Hello!