Simplified error handling without goto

Before ‘goto’ became a no-no, a common way of error handling was a jump on an unexpected return of a function. Max Vilimpoc gives a good example here: https://vilimpoc.org/research/raii-in-c/. The pattern used is the same in this simplified version:

int main() {
    resource_t *res_1 = init_1();
    if ( ! res_1 ) goto cleanup_end;

    resource_t *res_2 = init_2();
    if ( ! res_2 ) goto cleanup_res_1;

    resource_t *res_3 = init_3( res_1, res_2 );
    if ( ! res_3 ) goto cleanup_res_2;

    // do something with the resources

cleanup_res_3:
    deinit_3( res_3 );
cleanup_res_2:
    deinit_2( res_2 );
cleanup_res_1:
    deinit_1( res_1 );
cleanup_end:
    return 0;
}

A sequence of nested if-statements for error handling is logically similar, but far less elegant. When the code becomes more complex it will become harder to keep track of the nesting levels.

int main() {
    resource_t *res_1;
    resource_t *res_2;
    resource_t *res_3;

    res_1 = init_1();
    if ( res_1 ) {
        res_2 = init_2( res_1 );
        if ( res_2 ) {
            res_3 = init_3( res_1, res_2 );

            // do something with the resources

            if ( res_3 ) {
                deinit_3( res_3 );
            }
            deinit_2( res_2 );
        }
        deinit_1( res_1 );
    }

    return 0;
}

Error handling without goto

It is not that hard to rewrite the ‘goto’ implementation without the use of that keyword if you really have to (e.g. due to standard compliance). The cost is some extra lines of code and a possible reduction in performance. The trick is to separate the sequence in separate states indicating the progress made (e.g. in terms of resources that need clean-up). These states can be modelled as an enumeration. Loop over the states and break in case of an error. The reverse loop (starting at the current state) can be used for clean-up actions.

int main() {
    resource_t *res_1;
    resource_t *res_2;
    resource_t *res_3;
    
    enum {
        state_begin = 0        ,
        state_res_1_initialised,
        state_res_2_initialised,
        state_res_3_initialised,
        state_last
    } state = state_begin;
    
    for ( ; state < state_last; ++state ) {
        if ( state == state_begin ) {
            res_1 = init_1();
            if ( ! res_1 ) {
                break;
            }
        }
        if ( state == state_res_1_initialised ) {
            res_2 = init_2( res_1 );
            if ( ! res_2 ) {
                break;
            }
        }
        if ( state == state_res_2_initialised ) {
            res_3 = init_3( res_1, res_2 );
            if ( ! res_3 ) {
                break;
            }
        }
        if ( state == state_res_3_initialised ) {
            // do something with the resources
        }
    }

    for ( ; state > 0; --state) {
        switch( state ) {
            case state_res_3_initialised :
                deinit_3(&res3);
                break;
            case state_res_2_initialised :
                deinit_2(&res2);
                break;
            case state_res_1_initialised :
                deinit_1(&res1);
                break;
             default:
                break;
        }
    }
    return 0;
}

A modified version of Max Vilimpoc’s code that follows this approach can be found here. I would have preferred to model the resource allocation part as a switch statement too, but this would have made it impossible to use ‘break’ to exit the loop. Note that the use of ‘break’ and ‘continue’ may also be against some coding standards, but these keywords can be easily avoided by an extra control variable in the for-loop. What did we gain with this exercise? We got rid of ‘goto’, which makes it easier to comply to some standards and we made the flow a bit more predictable by eliminating infinite loops by design. There are no backward jumps possible and all for loops are bounded. What did we lose? We need a lot more lines of code and the administration of the state and extra comparisons may come at some performance cost. Was it worth it? Probably only if you are (or need to be) pedantic about not using ‘goto’.

Simplified error handling

My preferred way of error handling to perform each step that needs error handling in a separate function with an inout error parameter. If on input the error value indicates success the function performs its regular job, otherwise it returns immediately without changing the error parameter. It is straightforward to wrap existing functions to conform to this interface.

resource_t *wrapped_init_1(error_t *pError) {
    if ( *pError != ERROR_NONE )
        return NULL;

    resource_t *ret = init_1();
    if ( ! ret ) {
        *pError = ERROR_RESOURCE_1_FAILED;
    }

    return ret;
}

This way the functions can simply be used in a sequence. Errors can easily be checked for at the end of the sequence. If you need to do (conditional) clean-up, it is easier to check whether the resource is allocated rather than checking the state of progress so far. For example, use null to initialise a pointer to memory. In the clean-up section you can just free the memory (posix free with null as parameter has no effect). It is uncommon to report errors on deallocating resources (you are already done with the work, just deallocate if possible). For robustness it is best to modify the (reference to a) resource such that it is easy to check whether the resource is still allocated. For example:

void wrapped_free(void **p) {
    free(*p);
    *p = NULL;
}

Depending on the situation you may still want to log or assert whenever an attempt is made to deallocate a resource which has not been allocated in the first place. As for error reporting you can simply pass (preferably as inout parameter) the error parameter.

int main() {
    error_t  error;

    resource_t *res_1 = init_1( &error );
    resource_t *res_2 = init_2( res_1, &error );
    resource_t *res_3 = init_3( res_1, res_2, &error );

    // do something with the resources

    deinit_3(&res3);
    deinit_2(&res2);
    deinit_1(&res1);

    return -error;
}

To show the feasibility of this approach you can find a modified version of Max Vilimpoc’s code with simplified error handling here. The number of code lines increases a bit due to the required wrappers for library functions. In real life situations you will find the lines of code will decrease as the wrappers can be reused and the main flow is much simpler!

A different approach to state machines – part 3

Last time we applied some code size optimization to our state machine implementation. In this part we improve the maintainability (and arguably readability) of the code. The code size won in the previous post came at the expense of some extra lines of source code in the form of the guard and effect handlers. Luckily, there exists a little-known pre-processor trick that can help us here: ‘X Macros’. In essence an X Macro is a function-like macro with another function-like macro as argument. The replacement token list is a list of ‘calls’ to the function-like macro with one or more ‘regular’ parameters. Are you still with me? Ok, let’s see an example:

#define EFFECTS( FUN )       \
    FUN( SetDefaultOnLevel ) \
    FUN( TurnOff )           \
    FUN( DimUp )             \
    FUN( DimDown )           \
    FUN( StopDimming )

// add 'e' prefix to the effect name
#define ENUM_ITEM( item ) e##item,

// create a function prototype from the effect name with prefix 'handle'
#define PROTOTYPE_ITEM( item ) static void handle##item(void);

// create a case statement for effect in which the corresponding function
// is called
#define CASE_ITEM( item ) case e##item : handle##item(); break;

// effects enumeration
enum {
    EFFECTS( ENUM_ITEM )
};

// effects function prototypes
EFFECTS( PROTOTYPE_ITEM )

// effect handler implementation
static void effectHandler(uint8_t effect) {
    switch( effect ) {
        EFFECTS( CASE_ITEM )
        default :
            break;
    }
}

Which expands to (layout adjusted for clarity):

// effects enumeration
enum {
    eSetDefaultOnLevel,
    eTurnOff          ,
    eDimUp            ,
    eDimDown          ,
    eStopDimming      , // I love how C99 allows this!
};

// effects function prototypes
static void handleSetDefaultOnLevel(void);
static void handleTurnOff(void);
static void handleDimUp(void);
static void handleDimDown(void);
static void handleStopDimming(void);

// effect handler implementation
static void effectHandler(uint8_t effect) {
    switch( effect ) {
        case eSetDefaultOnLevel : handleSetDefaultOnLevel(); break;
        case eTurnOff           : handleTurnOff()          ; break;
        case eDimUp             : handleDimUp()            ; break;
        case eDimDown           : handleDimDown()          ; break;
        case eStopDimming       : handleStopDimming()      ; break;
        default :
            break;
    }
}

The ENUM_ITEM, PROTOTYPE_ITEM and CASE_ITEM macros can be factored out in a separate file, which can be reused for multiple state machines. Note how we only need to add one line to the effects macro to add an effect, versus adding an enumeration, a function prototype and a case statement! The lines of code could be further reduced by factoring out the standard implementation of the effectHandler function, but I prefer to leave some clues about what is going on with all these macros. I feel their usage in this specific case pays off. Just make sure you do not go overboard with nasty pre-processor tricks; they can be notoriously hard to read and debug. The complete sources can be downloaded from statemachine_part3.zip

Indispensable links

Each software developer has a list of online resources that are essential for software design, development and everything related. Here, I am going to share mine:

  1. WayBackMachine – Information on the internet can be really volatile. On several occasions an interesting article or link to a useful tool was removed from a website by the time I really needed it. As a last resort you can always depend on the way back machine. Just enter the website and you’ll get a list of snapshots by date. This can be a real life saver! Or if you’re in a sentimental mood, just check what big websites looked like in the previous millennium. Time travel is here!
  2. plantUML Server – It can be a real pain to install java and graphviz. But why bother if you can use the excellent plantUML tooling online?
  3. cdecl: C gibberish ↔ English – Having issues reading a pesky c statement by some smart-ass developer? Here’s google translate for code. Ok, as an alternative you could of course learn the Clockwise/Spiral Rule.
  4. interactive c/c++ compiler – Does it even compile what I just wrote? What assembly will compiler X generate for this snippet? And what if I change the optimization level? Guess no more; you can experiment to your heart content online.
  5. Bit Twiddling Hacks – Ok, you will probably never NEED this one. Like no one ever needs a Tesla Roadster. But it is very neat. Prepare to be amazed by at leasts some of these ‘hacks’. If you sprinkle some of these in production code, you may want to make your variable/function naming pretty clear or even add a comment (shudder) to explain what you are doing to the unfortunate developer maintaining your code.

I left out the usual suspects, such as Wikipedia and Stack Overflow, which you probably use already. Feel free to share your favorite (online) resources.

A different approach to state machines – part 2

In the last part we looked at a different approach for implementing state machines. In this part we will refine the implementation with the goal descrease the code size. In this post, I discussed the advantage of using a switch statement over writing a jumptable yourself. Applying this to the statemachine implementation decreases the code size by replacing ‘costly’ function pointers with bytes. A further descrease is possible since the compiler can reduce the number of ‘actual’ (non-inlined) functions. The guard and effect functions will each be replaced by a single function containing a switch statement to select the correct guard/effect.

//
// Updates to types. Note that the role of guardHandler_t and effectHandler has
// changed. Members of these types have been added to statemachine_t. These
// functions are responsible for handling ALL guards and effects for this
// statemachine.
//
typedef bool (*guardHandler_t)(uint8_t guard);
typedef void (*effectHandler_t)(uint8_t effect);

typedef struct statemachineRule_t {
    uint8_t trigger;
    uint8_t guard;
    uint8_t effect;
    uint8_t state;
} statemachineRule_t;

typdef struct statemachine_t {
    guardHandler_t           guardHandler;
    effectHandler_t          effectHandler;
    const statemachineRule_t *pRules;
    const uint8_t            nrOfRules;
    uint8_t                  currentState;
}

// Example guard and effect handler. Note that the guard handler always returns
// true for unknown cases. This makes the implementation of the NONE and ELSE
// guards trivial. It will also help if we start introducing state entry and
// exit functions. Note that NONE and ELSE are identical from an implementation
// perspective, but mean something different from intent. NONE indicates there
// are no conditions to be met for applying the transition. ELSE indicates the
// transition should be taken if all the alternative conditions are false.
//
bool guardHandler(uint8_t guard) {
    switch( guard ) {
        case gLevelIsZero : return levelIsZero();
        default : return true;
    }
}

void effectHandler(uint8_t effect) {
    switch( effect ) {
        case eSetDefaultOnLevel : return setDefaultOnLevel(); break;
        case eTurnOff           : return turnOff()          ; break;
        case eDimUp             : return dimUp()            ; break;
        case eDimDown           : return dimDown()          ; break;
        case eStopDimming       : return stopDimming()      ; break;
        default : return true;
    }
}

// Updates to state machine handler. effectHandler and guardHandler are now
// members of statemachine and are allowed to be NULL. The guards and effects
// are identified by a byte and fed as a parameter to these functions.
//
static void statemachine_ApplyTrigger(
    statemachine_t *pStatemachine,
    trigger_t       trigger      ) {

    uint8_t internalState = INVALID;

    for ( size_t i = 0u; i < pStatemachine->nrOfRules; ++i ) {
        statemachineRule_t rule = pStatemachine->rules[i];
        if ( rule.trigger == INVALID ) {
            internalState = rule.state;
            continue;
        }

        if (   (internalState == pStatemachine->currentState)
            && (trigger == rule.trigger)
            && (   (pStatemachine->guardHandler == NULL)
                || pStatemachine->guardHandler(rule.guard)) ) {
        
            if ( pStatemachine->effectHandler ) {
                pStatemachine->effectHandler( rule.effect );
            }
            pStatemachine->currentState = rule.state;
            break;
        }
    }
}

As with the previous post you can find a complete compileable example here. A quick test with ‘gcc’ and ‘size’ shows a code size decrease of roughly 500 bytes on my machine which is 10% of the complete code size. Pretty spectacular if you consider the complete code already contains quite some overhead by linking to standard libraries. I will provide some more in depth analysis on code size when we proceed refining the implementation in the next part of this series.

$ gcc -o statemachine_part1 statemachine_part1.c -Os -std=c99
$ gcc -o statemachine_part2 statemachine_part2.c -Os -std=c99

$ size statemachine_part1 
   text    data     bss     dec     hex filename
   4561    2132     480    7173    1c05 statemachine_part1

$ size statemachine_part2 
   text    data     bss     dec     hex filename
   4081    2132     480    6693    1a25 statemachine_part2

A different approach to state machines

Especially useful for embedded control systems, state machines are an intuitive and effective tool for design and documentation. In this post I am going to describe an approach to implementing state machines I came up with a couple of years ago. Shortly after, I discovered very similar ideas have been in use in the industry for quite some time. Still, the approach does not seem to be commonly known. This is a real shame; the proposed implementation of state machines can lead to a roughly 2.5 times smaller code size, while being extremely easy to read and maintain.

Readability is actually the starting point of this exercise. What if we could just execute our state machine design? As an example we will use the state diagram below. I use hungarian notation to differentiate (s)tates, (t)riggers, (g)uards and (e)ffects. This will become more helpful once we refine the state machine implementation later on [1].

statemachine

Jotting down circles and arrows is not very convenient in text [2], but state machines can be easily expressed in text as well, see below.

sOff :
    tPressOn                    / eSetDefaultOnLevel -> sOn
sOn :
    tPressOff                   / eTurnOff           -> sOff
    tPressUp                    / eDimUp             -> sDimming
    tPressDown                  / eDimDown           -> sDimming
sDimming :
    tPressOff                   / eTurnOff           -> sOff
    tReleaseUp                  / eStopDimming       -> sOn
    tReleaseDown [gLevelIsZero] / eStopDimming       -> sOff
    tReleaseDown [gELSE]        / eStopDimming       -> sOn

Theoretically, we could already write this format as a multi-line string in a source file, parse and interpret it. This, however, would be very inefficient in terms of code size and execution speed. As an alternative write the state machine in tabular format as an array of structs:

typedef bool_t (*guardHandler_t)(void);
typedef void (*effectHandler_t)(void);

typedef struct statemachineRule_t {
    uint8_t         trigger;
    guardHandler_t  guard;
    effectHandler_t effect;
    uint8_t         state;
} statemachineRule_t;

#define INVALID 0xFFu
#define STATE( s ) { INVALID, NULL, NULL, s }

enum states {
    sOff    ,
    sOn     ,
    sDimming
};

enum triggers {
    tPressOn    ,
    tPressOff   ,
    tPressUp    ,
    tPressDown  ,
    tReleaseUp  ,
    tReleaseDown
};

const stateMachineRule_t rules[] =
{
//    trigger     , guard       , effect            , next state
STATE( sOff ),
    { tPressOn    , NONE        , eSetDefaultOnLevel, sOn        },
STATE( sOn ),
    { tPressOff   , NONE        , eTurnOff          , sOff       },
    { tPressUp    , NONE        , eDimUp            , sDimming   },
    { tPressDown  , NONE        , eDimDown          , sDimming   },
STATE( sDimming ),
    { tPressOff   , NONE        , eTurnOff          , sOff       },
    { tReleaseUp  , NONE        , eStopDimming      , sOn        },
    { tReleaseDown, gLevelIsZero, eStopDimming      , sOff       },
    { tReleaseDown, ELSE        , eStopDimming      , sOn        }
}

Note how close the implementation is to the original design! There is one things left to do; implement the engine for the state machine. Luckily, this is surprisingly simple for this first iteration.

typedef struct statemachine_t {
    const stateMachineRule_t rules[];
    const uint8_t            nrOfRules;
    uint8_t                  currentState;
} statemachine_t;


void ProcessTrigger(
    stateMachine_t  *pStateMachine,
    const trigger_t trigger       ) {

    uint8_t internalState = INVALID;

    for ( size_t i = 0u; i < pStateMachine->nrOfRules; ++i ) {
        stateMachineRule_t rule = pStateMachine->rules[i];
        if ( rule.trigger == INVALID ) {
            internalState = rule.state;
            continue;
        }

        if (   (internalState == pStateMachine->currentState)
            && (trigger == rule.trigger)
            && rule.guard() ) {
        
            rule.effect();
            pStateMachine->currentState = rule.state;
            break;
        }
    }
}

I left out some minor details such as the guard and effects functions. Here is a completely self contained .c file that can be compiled into an executable example. In practice you will want to put the state machine engine definitions and implementation in a separate component. This is one of the refinements we will make in a next post.


1. I usually avoid Hungarian notation for indicating types. Here, however, it serves a dual purpose. The descriptive names of state, triggers, guards and effects are likely to clash with existing function or variable names. The prefix prevents most of the clashes. It al

Some love for the conditional operator

The conditional (ternary) operator almost falls in the same category as goto in that it its use is frowned upon and can be easily circumvented. Go To Statement Considered Harmful by Edsger Dijkstra snowballed, despite some good arguments (see Structured Programming with go to Statements by Donald E. Knuth), into banning goto from most workplaces. This fate has not (yet) been shared by the conditional operator, but there are certainly voices out there that would like to see this happen [1] (apologies for the reference).

Admittedly, the conditional operator is something like an acquired taste. In the beginning of my professional career, I too, used to prefer the much more verbose and therefore arguably more readable if – else statements. Reading the conditional operator is like reading the ramblings of three grunting cave men. The first questioning a statement, the second giving the answer should the statement be true and the third mentioning the alternative.

The formatting is heavily inspired by an article of Anders Lindgren, IAR Systems “Thoughts on readable code: Conditional expressions”, which in turn seems to be inspired by reverse polish notation. The article does not seem to be available anymore. I had to dig it up via the wayback machine. It is a short but good read. It may be another acquired taste, but the formatting rules are simple and allow one to read complex expressions and statements that would otherwise be incomprehensible.
ans =  Huh   // cave man 1: Huh?
     ? Duh   // cave man 2: Duh!
     : Doh;  // cave man 3: Doh.

If – else seems Shakespeare next to this. Then again, one could get bored reading Shakespeare. Nothing wrong with a pulp fiction page-turner, especially if it is about grunting cave men (Dan Brown owes me for this idea). There are a small number of situations where I prefer the use of the conditional operator over the alternatives. One such case is conditional initialization of variables.

Compare :

// Get the (4-bit) values for trigger, guard, effect and state from an array
// of bytes. 'idx' is an offset in the array, 'nibIdx' is a relative offset
// to the nibble (4-bit). Type is a 4-bit bitmask indicating what data to
// expect in the byte array. If the data is present the corresponding bit in
// type is set and the value is read. Otherwise the value is INVALID.

uint8_t trigger = INVALID;
uint8_t guard   = INVALID;
uint8_t effect  = INVALID;
uint8_t state   = INVALID;

if ( type & triggerBit ) {
    trigger = GetValue(pFsm, idx, nibIdx++);
}
if ( type & guardBit ) {
    guard = GetValue(pFsm, idx, nibIdx++);
}
if ( type & effectBit ) {
    effect = GetValue(pFsm, idx, nibIdx++);
}
if ( type & stateBit ) {
    state = GetValue(pFsm, idx, nibIdx++);
}

against :

    
// Get the (4-bit) values for trigger, guard, effect and state from an array
// of bytes. 'idx' is an offset in the array, 'nibIdx' is a relative offset
// to the nibble (4-bit). Type is a 4-bit bitmask indicating what data to
// expect in the byte array. If the data is present the corresponding bit in
// type is set and the value is read. Otherwise the value is set to INVALID.

uint8_t trigger, guard, effect, state;

trigger = (type & triggerBit ) ? GetValue(pFsm, idx, nibIdx++) : INVALID;
guard   = (type & guardBit   ) ? GetValue(pFsm, idx, nibIdx++) : INVALID;
effect  = (type & effectBit  ) ? GetValue(pFsm, idx, nibIdx++) : INVALID;
state   = (type & stateBit   ) ? GetValue(pFsm, idx, nibIdx++) : INVALID;

Note that this example contains a number of things that in general could be considered bad practice. The multiple declarations on a single line are here to limit the width of the following lines for online readability. The use of the postfix increment (because of the side effect) inside a function is usually not a good idea. In my opinion the ‘sin’ improves the brevity and readability of this code snippet. This approach is also often used for checking whether a value is null. So often that C#, Perl, and Swift introduce a null coalescing operator. Another case where the conditional operator comes in handy is when calling a function with a large set of parameters of which one parameter is conditional.

Compare :

float angle = 0;
if ( hidden ) {
    angle = M_PI;
}

RotateTransform( &transform,  // a 3D transform
                 1         ,  // x-axis
                 0         ,  // y-axis
                 0         ,  // z-axis
                 angle     ); // rotation angle

or worse :

if ( hidden ) {
    RotateTransform( &transform,  // a 3D transform
                     1         ,  // x-axis
                     0         ,  // y-axis
                     0         ,  // z-axis
                     M_PI      ); // rotation angle 
} else {
    RotateTransform( &transform,  // a 3D transform
                     1         ,  // x-axis
                     0         ,  // y-axis
                     0         ,  // z-axis
                     0         ); // rotation angle 
}

against :

RotateTransform( &transform        ,  // a 3D transform
                 1                 ,  // x-axis
                 0                 ,  // y-axis
                 0                 ,  // z-axis
                 hidden ? M_PI : 0 ); // rotation angle

Especially if the code is part of a larger function the latter approach immediately conveys the similarities and differences between both branches.

Sections in source code

As a software developer you will need to read code more often than you write it. Applying a consistent structure to your source code makes it easier to navigate and maintain your sources. Section headers are a great help in navigating your sources and also help in creating a consistent structure among co-workers. The order of sections is not trivial to get right. Sections may have dependencies: constants can refer to function declarations, function definitions refer to constants, etc. Sections also have a logical ordering: definitions of interface functions are more important than local functions and should be featured more prominently. The following snippets define the order of sections I apply to my sources. The ordering satisfies all possible/usefull dependencies (with one exception I know of) and prominently features the implementation of interfaces at the bottom of a source file. Due to dependencies it is not feasible to have the implementation of the interface at the top of the source file. Luckily, from a stylistic perspective (see Strunk & White), the bottom of the file makes sense. The include files at the top of the file gives a nice overview of the required interfaces. It may be necessary to add a macro before including a file, since the macro should be applied to that file as well. This is the one exception to the section dependencies I mentioned earlier. In this rare case I add the macro just before including the file, leaving the sections as is. This gives a nice visual clue that it is an exception rather than a rule.

Header file (.h):

///////////////////////////////////////////////////////////////////////////////
// INCLUDES
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// MACROS
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// TYPES
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// FUNCTION PROTOTYPES
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// CONSTANTS AND VARIABLES
///////////////////////////////////////////////////////////////////////////////

Source file (.c):

///////////////////////////////////////////////////////////////////////////////
// INCLUDES
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// LOCAL MACROS
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// LOCAL TYPES
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// LOCAL FUNCTION PROTOTYPES
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// LOCAL CONSTANTS AND VARIABLES
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// LOCAL FUNCTIONS
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// EXPORTED CONSTANTS AND VARIABLES
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// EXPORTED FUNCTIONS
///////////////////////////////////////////////////////////////////////////////

To switch or not to switch…

Nigel Jones makes a compelling argument against the use of switch statements. A compiler has roughly two options for implementing a switch statement: 1) the equivalent of a number of if – else if – else statements or 2) a jump table. For a small number of cases option 1) may be more efficient. If you have a large number of cases option 2) is likely to be more efficient. Some compilers only support one option, others apply heuristics to choose between the alternatives. The bottom line of Nigel’s argument is the actual code size of a switch statement is rather unpredictable. This can be especially confusing if you remove a case and find the code size increases due to the compiler now favoring option 1) over option 2). However, the code size for a switch statement is always comparable or less than an equivalent series of if – else if – else statements. For me, that is good enough to generously apply the switch statements in my code. Furthermore, I find the syntax of the statement aesthetically pleasing and thus easy to read. Forgetting an ‘else’ in a sequence of if – else if – else statements can be a nasty bug to find. I find the equivalent in a switch statement, a missing break, much harder to overlook. But hold on a second, what if you implement a jump table yourself? As the following snippet shows, a jump table is also very easy to read.

typedef enum {
    action_turnOn      = 0,
    action_turnOff     ,
    action_dimUp       ,
    action_dimDown     ,
    action_stopDimming
} action_t;

static void TurnOn(void);
static void TurnOff(void);
static void DimUp(void);
static void DimDown(void);
static void StopDimming(void);

//
// SWITCH STATEMENT
//
void ExecuteAction(action_t action) {
    switch( action ) {
        case action_turnOn      : TurnOn()     ; break;
        case action_turnOff     : TurnOff()    ; break;
        case action_dimUp       : DimUp()      ; break;
        case action_dimDown     : DimDown()    ; break;
        case action_stopDimming : StopDimming(); break;
        default:
            break;
    }
}

//
// IF - ELSE IF - ELSE SEQUENCE
//
void ExecuteAction(action_t action) {
    if ( action == action_turnOn ) {
        TurnOn();
    } else if ( action == action_turnOff ) {
        TurnOff();
    } else if ( action == action_dimUp ) {
        DimUp();
    } else if ( action == action_dimDown ) {
        DimDown();
    } else if ( action == action_stopDimming ) {
        StopDimming();
    }
}

//
// JUMP TABLE
//
void ExecuteAction(action_t action) {
    void (*jumpTable[])(void) = {
        TurnOn         ,
        TurnOff        ,
        TurnDimUp      ,
        TurnDimDown    ,
        TurnStopDimming
    };
    
    jumpTable[action]();
}

Unfortunately the compiler has more freedom in implementing a jump table than you have at the source level. You will have to create functions and reference them by pointers, which prevents the compiler from inlining them. The functions themselves may cause some administrative overhead. If the functions are inlined, which can be the case in a switch statement, it is easier for the compiler to exploit common parts and do a better job at optimizing for small code size. Not all jumps are equal, a local jump within a function may be less costly than a function call. The result is that a jump table is probably more costly in terms of code size than a switch statement or an if – else is – else sequence. That said, jump tables CAN be a good alternative. Switch statements only work with constant numbers. With some modifications jump tables can work with other data types as well. Measure what works best for you and your platform. At least you now have the choice between a sequence of if – else if – else statements, a switch statement or a jump table.

Code size optimization

Code size (ROM) should generally not be the first thing on your mind. If you are in a position to pick a platform where you are not likely to run into code size issues, do so. Optimizing for smaller code size is a time-consiming (read expensive) task, which does not add value to your project. Smaller code size is often traded against longer execution times. Longer execution times also means more power consumption. Even if your device is mains-powered, it does not hurt minding our planet a little, does it?

It is good practice to start your project with compiler settings that are a healthy mix of optimization for speed and the ability to debug. If code size becomes an issue, turn off settings that trade speed against code size (e.g. loop unrolling). If compiler output is already optimized for smallest code size, you will need some additional tricks up your sleeve. Unfortunately there are no common patterns for code optimization on source level. Code size is not directly proportional to physical lines of code. Results are largely dependent on the platform used. You could lose a lot of effort spent on reducing code size if you switch to a different platform.

That said, there will be times in which your application just does not fit in ROM and you do not have the luxury of switching platforms. Having a set of tools to decrease code size will definitely come in handy. I intend to create a series of posts here to describe some techniques you can apply to decrease code size, but I should start with a warning: your milage may vary. Do not blindly depend on my advice or your intuition, but remember the most important engineering principle: measure! Always compare code size of various options in your situation.