Preprocessor – the #error Directive

Preprocessor – the #error Directive

This is a very useful and often underused preprocessor directive.

Behaviour of this preprocessor directive is the same for both C and C++ compilers.

Purpose

The #error directive terminates compilation and outputs the text following the directive.

Format

#error text

All preprocessor directives begin with the # symbol. It must be the first character on the line or the first character on the line following optional white space.

Some early compilers flagged an error if # was not the first character on the line.

Spaces or tabs are permitted between the # and error, but not escape characters or other symbols or macros. The preprocessor removes white space and concatenates the # and error together.

If anything follows the #error directive (other than white space) then the program is malformed.

The following are valid uses:

#error some error message text
# error some error text to display
# /* comments are white space */ error some error message to display

The following are invalid uses:

// #\ is not a valid preprocessor directive
# \t error text to output
// #" is not a valid preprocessor directive
# "" text to output

Use

It is used to render a program malformed and output the text following the #error directive. The text may be quoted or unquoted (it doesn’t matter). No macro expansion takes place.

The language specifications do not say how the text following the #error directive is to be treated.

The GCC compiler, replaces all white space characters between tokens with a single white space character.

I have no reason to believe other compilers behave differently since white space is not considered significant in the C and C++ languages – it serves only to seperate tokens from one another.

There are many times when it is useful to halt compilation:

  1. code is incomplete
  2. code requires particular library versions
  3. code uses compiler dependent features
  4. code has specific compiler requirements

Incomplete Code

When developing code, it is common to create stub functions. For the final release, these stub functions need to be implemented. We can let the compiler help us catch unimplemented functions:

int my_function( void )

{

#error my_function not implemented

return 0;

}

The above code will fail for every compile. It might be more useful to allow compiling during development, but break the compile when we try to compile a release version. In the following example, we assume that during development, the macro DEBUG is defined:

int my_function( void )

{

#ifndef DEBUG

#error my_function not implemented

#endif

return 0;

}

During development, we can compile the code, but when we do a release build (one in which DEBUG is not defined, then we catch unimplemented functions.

Version Checking

Sometimes code is dependent on particular versions of a library. It is useful to be able to stop compilation if an incorrect library version is included:

#if library_version < 2

#error requires library_version 2 or better

#endif

Compiler Dependency

Sometimes, code uses compiler specific features (for example, GCC allows nesting a #define within another #define – this is a non-standard compiler feature).

#ifdef __GCC__

// some GCC specific code goes here

#else

#error requires GCC compiler to compile

#endif

Compiler Requirements

Sometimes code makes relies on certain assumptions about the target environment (for example, the size of an integer):

#include

#if UINT_MAX != 4294967295

#error this application requires 32 bit integers

#endif