Robust C Code Part 3 – Wrapping C
In the last segment in this series “Robust C Code Part 2 – Advanced C Preprocessor” I talked about the preprocessor concatenation directive. In this segment I will talk about using the preprocessor to wrap C code, and what an L-value is. Now this is getting a little technical, so you might want to brush up on C.
The macros I defined in parts 1 and 2 of the series are rather simple. Many times I want to wrap multiple C statements in a macro and use them just as a regular statements. This seems rather straight forward we could do the following
#define setup_some_hardware() hw_register1=0x12;hw_register2=0x34;hw_register3=0x56
This will work most of the time since the statements are executed one after another, however this is dangerous because the statements are not grouped in the same scope. You can think of scope as groups of variables that can ‘see’ each other. If you have 2 functions, one with ‘int x’ inside and one with ‘int y’ inside, the variables can not ‘see’ each other since they are are in different functions, which means you can’t do x=y; they exist in different scope. A new scope can be created with curly braces {}, which means loops, if statement and functions all have their own scope. The three statements in the previous macro can exist in different scopes like in this example:
// Check the hardware and enabled if off
if(HWREGISTER.ON == false)
setup_some_hardware();
An if() statement without curly braces {} will execute the single next statement if the condition is true. This causes a problem because the preprocessor expands the macro to:
// Check the hardware and enabled if off
if(HWREGISTER.ON == false)
hw_register1=0x12;hw_register2=0x34;hw_register3=0x56
This results in hw_register2 and hw_register3 ALWAYS being set and only hw_register1 affected by the if() statement. In C you can’t have semicolons in parentheses, so what do you do to group the statements? One solution is to add curly braces {} to macros that don’t return a value so all statements are in the same scope. This seems logical however in C you can’t have a semicolon after a brace which means you have some statements with semicolons and some without, which can lead to some really hard to find bugs.
The solution for this problem is to wrap the multiple statements in do{ }while(0). If you took CS101, the do{}while() loop will always allow one execution before evaluating the condition in the while(). Since while(0) is always false any good ANSI C preprocessor will remove the do{}while(0) and only execute the code once. Since the loop uses curly braces to create a new scope, all statements are in the same scope and a semicolon can be used on the statement.
#define setup_some_hardware() do{hw_register1=0x12;hw_register2=0x34;hw_register3=0x56;}while(0)
// Check the hardware and enabled if off
if(HWREGISTER.ON == false)
setup_some_hardware();
An additional benefit of the scope created by the do{ }while(0) is that new variables can be created in the new scope.
#define swap_int(val1,val2) do{int tmp; tmp=val1; val1=val2; val2=tmp;}while(0)
if(some_condition)
swap_int(x,y);
A little known/understood element in C is the L-value. L-values can be though of as the value returned from the right side of a C statement to the left side of the statement. a do{ }while() loop has no L-value and will result in a compiler error if an L-value is expected, so any statement that returns a value should NOT use do{ }while(0) wrapping. A good rule of thumb is if the statement returns a value use parathenses, if it doesn’t, use do{ }while(0).
The following is an excerpt from a UART C header which uses the preprocessor to give more developer friendly names to sequences of statements and convoluted register names:
#define uart1_interrupt_rx_enable() do{IEC0bits.U1RXIE = 1;}while(0)
#define uart1_interrupt_rx_disable() do{IEC0bits.U1RXIE = 0;}while(0)
#define uart1_interrupt_rx_clear() do{IFS0bits.U1RXIF = 0;}while(0)
#define uart1_interrupt_rx_set() do{IFS0bits.U1RXIF = 1;}while(0)
#define uart1_interrupt_tx_enable() do{IEC0bits.U1TXIE = 1;}while(0)
#define uart1_interrupt_tx_disable() do{IEC0bits.U1TXIE = 0;}while(0)
#define uart1_tx_buffer_full() (U1STAbits.UTXBF == 1)
#define uart1_tx_last_done() (U1STAbits.TRMT==1)
#define uart1_rx_overrun_get() (U1STAbits.OERR)
#define uart1_reset() do{U1MODE = 0;U1STA = 0;}while(0)
#define uart1_enable() do{U1MODEbits.UARTEN = 1;}while(0)
All the preprocessor macros that I have shown will compile down into “in line” instructions. This speeds up execution compared to functions since the processor doesn’t have to create a stack frame and branch to another location in code just to do a simple bit set. The one downside is highly used macros with multiple statements will be injected into the source each time they are used, which can lead to larger code. I try and keep preprocessor macros short and simple to reduce this.