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 - Basic Infomation (GNU Preprocessor)

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

Contents

How Preprocessor Works
Macrodefinitions (macros)
Macro Arguments
Variable Number of Arguments (Variadic Macros)
Transformation to Strings (Stringification)
Merging Symbols (Concatenation)
Predefined Symbols
Details of Preprocessor (another page)
Preprocessor - Advanced Usage (another page)

How Preprocessor Works

The preprocessor is run before compilation of C/C++ source files. It is responsible for:

  • including header files (#include)
  • conditional enabling and disabling parts of the source files (#ifdef etc.)
  • filtering comments /**/, //
  • expanding macrodefinitions

Please note that the preprocessor does not recognize keywords of the programming languages (C/C++), because they are compiled in next stage. Moreover, preprocessing is some kind of text transformation and its done in this domain. The preprocessor has simplified expressions and limited number of operators. They are generaly oriented to comparison integer values.

Macrodefitions (macros)

There is no doubt that the most interesting and powerful feature of the preprocessor is macrodefinition. They can replace parts of a text by another textual contents. Macrodefinition processing is explained in details in another page devoted to the preprocessor.
Because the macros are expanded at compilation time (but not at run time) can save a lot of target CPU load, because code is transformed earlier and ready to execute. Of course, these must be parts of the source that is known at building time (i.e. does not depend on run time)
Although simplicity the preprocessor offers powerful options for making coding easier. A programmer can be supported by the preprocessor and helps him avoid entering the same contents in multiple places (if something is entered in at least two places sooner or later it will be desynchronized). The preprocessor can be even used for building some kind of metalanguage that extends features of the prorgammign language or hides some implemenetation details. The macros can be applied in specification in some custom implementation. A general library can be developed without knowledge of specific low level functions, that are provided until someone need to use it. For example, some library can use synchronization mechanism (e.g. mutexes) but implementation of mutexes will be added by a programmer that needs to use the library.

Macro Arguments

There are two kinds of macros with or without arguments:

  • function-like macros.
  • object-like macros,
#define MY_TABLE_SIZE 256

#define SATURATE(x) (x>255)? 255 : (x)

Note that there is no space between name of macro and opening parenthesis! If you insert the space the macro is recognized as object-like (i.e. argument-less) and argument list becomes part of the expansion:

//incorrect definition because of the space after macro name:
#define SATURATE (x)   (x>255)? 255 : (x)

//usage:
y = SATURATE(w);

//is expanded to:
y = (x)  (x>255)? 255 : (x)(w);

//instead of expected:
y = (w>255)? 255 : (w);

Variable Number of Arguments (Variadic Macros)

In a case of function-like macro, number of used arguments must be consistent with the number that were specified in the definition. Any inconsistence generates preprocessing error.

#define MACRO(x,y) (x)+(y)

w = MACRO(1);
// generates the error:
// error: macro "MACRO" requires 2 arguments, but only 1 given

w = MACRO(1,2,3);
// error: macro "MACRO" passed 3 arguments, but takes only 2

Of course, it is good that the preprocessor checks this consistency but sometimes it is useful to have a macro with not specified (variable) number of arguments. Here is the syntax for this:

#define DBG_MSG(level,format_string,...) printf( "<%d>" format_string, level,__VA_ARGS__ )

As you can see, variable number of arguments is signaled by ellipsis. In a place when they are needed __VA_ARGS__ symbol is used. After some trials of the example presented above, it can be noticed that format_string argument must be followed by at least one. Otherwise, the error is generated:

error: expected expression before ')' token
 #define DBG_MSG(level,format_string,...) printf( "<%d>" format_string, level,__VA_ARGS__ )
                                                                                          ^
  note: in expansion of macro DBG_MSG
  DBG_MSG(1, "some_string")
  ^

Concatenation operator (double hash - ##) can help is this problem. It will be discussed below. Here it can be interpretted as merging __VA_ARGS__ with comma. When __VA_ARGS__ is empty the comma is dropped.

#define DBG_MSG(level,format_string,...) printf( "<%d>" format_string, level, ## __VA_ARGS__ )

Transformation to Strings (Stringification)

The preprocessor provides a feature of transformation the symbol to a string. Let's take an example of a table that contains error values and its names (the names are required for printing error messages):

#define ERRCODE_DESC( x ) { x, #x }

typedef struct errtab_entry{
	int code;
	const char* name;
	} errtab_entry_t;

#define MYERROR1 1000
#define MYERROR2 2000

errtab_entry_t errtab[]={
	ERRCODE_DESC( MYERROR1 ),
	ERRCODE_DESC( MYERROR2 ),
	...
	};
//the table can be created by following code:
errtab_entry_t errtab[]={
	{ 1000, "MYERROR1" },
	{ 2000, "MYERROR2" },
	...
	};

Merging Symbols (Concatenation)

The concatenation operator (double hash) merges two tokens in the macro expansion:

#define CONCAT( x,y ) x##y 

//the macro usage:
CONCAT(a,b)
 
//will be expanded to:
ab

It can be surprising when another preprocessor symbol is used as argument in the macro. The result is that the symbol is not expanded before concatenation and its name is inserted.

#define my_symbol 100
//usage:
CONCAT(q,my_symbol)
 
//will be expanded to:
qmy_symbol

//instead of expected:
q100

In order to achieve expected aim, another extra macro is required. It will be inserted in expansion chain, hence it expands argument before it is used by concatenation.

#define _CONCAT( x,y ) x##y 
#define CONCAT( x,y ) _CONCAT(x,y)

#define my_symbol 100
//this time following:
CONCAT(q,my_symbol)
 
//is expanded to:
q100

Predefined Symbols

The preprocessor has predefined useful symbols:

-- SYMBOL --      --Description--            --Example of expansion--

__LINE__       current line number           37
__FILE__       current suorce file          "test.c"
__DATE__       current date                 "Dec 17 2014"
__TIME__       current time                 "10:39:02"
__COUNTER__    usage counter (incremented after each usage)     0

These symbols can be used for:

  • identification a location of error (line number, file)
  • marking version of a program - a compilaton time,
  • creating unique, consecutive ids.

Links

  1. The Preporcessor Documentation (on gcc/GNU site)