More about ELESOFTROM Company

ELESOFTROM Company is specializing in firmware development and fixing for embedded systems.

We have more than 10-years of experience in that area.

In developed tasks and projects we value reliability and effectivness of the firmware.

Fixing the Software for Emebedded Systems

Software for Microcontrollers



Home page

Full offer

Experience

About company

Contact


DioneOS - RTOS for embedded devices

ELESOFTROM developed RTOS for ARM Cortex-M3 and msp430.

The system is optimized for short execution time, having short switching time between threads.
The system provides elements (e.g. semaphores, mutexes, timers, queues etc.) used for building multi-threaded firmware.

Cortex version has full tests coverage and was tested by automatic testing framework.

Read more:

DioneOS home page

Reliability

Performance

DioneOS documentation

Tutorials about DioneOS


C Preprocessor - Advanced Usage (GNU Preprocessor)

2014-12-18    Piotr Romaniuk, Ph.D.

Contents

Introduction
Symbolic Identifier
Forcing Semicolon
Macro Selection by Parameter Value
Determining Number of Arguments
Checking if Arguments Were Specified
The Preprocesor - Basic Information (another page)
Details of Preprocessor (another page)

Introduction

The preprocessor can be used for generating parts of the code at compilaton time. Although its features seems to be very limited, the preprocessor can provide metalanguage that extends C programming. A lot of interesting and surprising ideas may be found in Boost Library, in a part devoted to preprocessor. The main trick consists in multiple enumeration of result for each argument value and specific behavior in macro expansion (including concatenation operator).
Some valuable examples are presented below:

Symbolic Identifier

If firmware is designed for multiple hardware platform or device types it is often necessary to select corresponding configuration. It is performed by conditional enabling parts of the code that is a version for particular device. For example, GPIO assignment may be required:

// Specification of device type (in specific header):
#define IDBOARD "CNC_ROUTER_HEAD3"

// following part is in common header:
#if IDBOARD=="CNC_ROUTER_HEAD3" 
# define MOTOR_PIN 1
# define MOTOR_PORT 1
#elif IDBOARD=="CNC_ROUTER_CUTTER"
# define MOTOR_PIN 3
# define MOTOR_PORT 1
#endif

// compilation fails with error:
test.c:114:12: error: token ""CNC_ROUTER_HEAD3"" is not valid in preprocessor expressions
#define IDBOARD "CNC_ROUTER_HEAD3"
                ^
test.c:120:5: note: in expression of macro 'IDBOARD'
 #if IDBOARD=="CNC_ROUTER_HEAD3"

The reason is that the preprocessor have no string comparison feature, hence expression in #if fails. It can compare only integer values. In order to solve that configuration type can be defined as macros (symbols) expanded to unique identifiers:

#define IDBOARD CNC_ROUTER_HEAD3

//identifiers for various types of devices:
#define CNC_ROUTER_HEAD3 1
#define CNC_ROUTER_CUTTER 2

#if IDBOARD==CNC_ROUTER_HEAD3 
# define MOTOR_PIN 1
# define MOTOR_PORT 1
#elif IDBOARD==CNC_ROUTER_CUTTER
# define MOTOR_PIN 3
# define MOTOR_PORT 1
#endif

Forcing Semicolon

When you use macros you may be not sure if semicolon should follow that or not. It would be nice to have it just because it recognizes syntax of C language. The most elegant way is when the macro implementer constructed it to force putting semicolon, if is omitted compiler error is generated. The problem is not trivial when multiline macros are considered and matching with cnditional instructions. It is required that the macro should be applied in any location (e.g. after if/else) without side effects and it should behave like a function.

// missing of integrity guarding
#define MULTILINE_MACRO( x ) fun( x );\
			     fun2()
	
//problem:
	if( a>3 ) MULTILINE_MACRO(a);
//is compiled to:
	if( a>3 ) fun( x );
	fun2();		  

Only first part of the macro (i.e. fun() call) is executed conditionally but the latter (i.e. fun2()) is always run. For sure it was not an intention of an author. Notice that program compiles without errrors but is not working properly. Let's add curly brackets and create complex instruction:

#define MULTILINE_MACRO( x ) {\
				fun( x );\
			     	fun2();\
			     }
	
	if( a>3 ) MULTILINE_MACRO(a);
//is compiled to:
	if( a>3 ) {
		fun( x );
		fun2();		  
		};

It is better but macro does not force programmer to put a semicolon. Moreover, there is no one rule if the semicolon should be or not at the end of macro. If the macro is before else the semicolon must not be used:

	if( a>3 ) MULTILINE_MACRO(a);
	else ...
//compilation fails:
test1.c:136:3: error: 'else' without a previous 'if'

The solution can be found in sources of the Linux kernel:

#define MULTILINE_MACRO( x ) \
	do{\
		fun( x );\
	     	fun2();\
	  }while(0)

This do-while envelope forces to put semicolon at the end, otherwise compilation fails. It works fine in any place also if/else parts. The loop run always once and only once.

Macro Selection by Parameter Value

Let's assume that some symbol is expanded to 0 or 1. We would like to have a macro that will be expanded to different contents depending on that parameter. The solution is to write two macros which names are result of concatenation a prefix (e.g. SELECT_) and value of controling symbol:

//selecting macro:
#define SELECT( sym_true, sym_false, ctrl ) _SELECT( sym_true, sym_false, ctrl )
#define _SELECT( sym_true, sym_false, ctrl ) SELECT_##ctrl( sym_true, sym_false )

//two macros for taking proper argument
#define SELECT_0( sym_true, sym_false ) sym_false
#define SELECT_1( sym_true, sym_false ) sym_true

//usage
#define SOME_CTRL_SYM 1
SELECT( SYMBOL_T, SYMBOL_F, SOME_CTRL_SYM )  => SYMBOL_T

//and when controling symbol is defined as 0:
#define SOME_CTRL_SYM 0
SELECT( SYMBOL_T, SYMBOL_F, SOME_CTRL_SYM )  => SYMBOL_F

Note that controling symbol may be a result of some another transformation. In the example presented above it is specified by #define only for illustration in simplest way how it works. The result of selective macro can be used in expansion of another macro building something more complex. There are many applications.

Determining Number of Arguments

Let's try to solve a problem of determination a number of macro arguments. The value may be used for selection of different behavior. Like before the selection can be obtained by concatenation a constant prefix and arguments number, so different macros names are produced. That different behavior for different number of arguments can be understood as simple overloading.

// let GET_ARGS_NB( ) is the macro which is expanded to number of its arguments

// firstly, we define concatenation that expands arguments as well 
// (the preprocessor does not this for concatenation)
#define _CONC( a, b ) a ## b
#define CONC( a, b ) _CONC( a, b )

// secondly, we define two different behaviors depending on argument number 
// (for demonstration purpose only for 1 i 2)
#define FN_ARGS_1( x )    some_fn_arg1( x )
#define FN_ARGS_2( x, y ) some_fn_args2( x, y )

// finally, a definition of the selector - 
// - main function-like macro used in code with variable number of arguments:
#define SOME_FN_VARARGS( ... ) CONC( FN_ARGS_, GET_ARGS_NB( __VA_ARGS__ ) )( __VA_ARGS__ )

// if the macro determining number of arguments works correctly the selector is expanded to:
SOME_FN_VARARGS( x ) 	  => some_fn_arg1( x )
SOME_FN_VARARGS( x, y )   => some_fn_args2( x, y )

Above discussion presented an example of application of the macro that determines number of arguments. Below the solution how to make it:

#define GET_6TH( n1, n2, n3, n4, n5, n6, ...) n6

#define GET_ARGS_NB( ... )  GET_6TH( __VA_ARGS__  , 5, 4, 3, 2, 1)

The idea consist in extending argument list by consecutive numbers. After that new list is used in another macro that takes a value from fixed position (here 6th argument). The more arguments is provided at the beginning the more these extra ones are shifted.

Checking if Argument Were Specified

Next problem to solve by preprocessor: check if the macro was invoked without arguments. Like before, different behavior may be implemented for usage with or without arguments.

#define GET_7TH( n1, n2, n3, n4, n5, n6, n7, ...) n7

#define IS_EMPTY( ... )  GET_7TH( 1000, ## __VA_ARGS__  , 0, 0, 0, 0, 0, 1, 1001)

// usage:
IS_EMPTY( a )	=> 0
IS_EMPTY( )     => 1

Number 1000 is only for having an argument before ## __VA_ARGS__ - this is necessary for swallowing a comma when no arguments are provided. Number 1001 provides an extra argument required for GET_7TH macro.
It is very nice that GNU preprocessor supports ##__VA_ARGS__, without that it would be necessary to build 7 macros and pretty complex transformations (refer to Boost Library).

Acknowledgments

I would like to thank dr Przemyslaw Baranski for interesting discussion about overloading in preprocessor - implementing different behavior depending on number of arguments.

Links

  1. The Boost Library
  2. Description of GNU C Preprocessor
  3. Description of Macro Arguments