Professional Documents
Culture Documents
OMS-Stand C Programming Rules
OMS-Stand C Programming Rules
Standard
C programming rules
Preface
In the Netherlands two advanced integrated modelling systems are operational for the prediction of
flow and transport phenomena of water related problems. At the Dutch Rijkswaterstaat (RWS) the
SIMONA system has been developed, whereas Delft3D is the integrated flow and transport modelling
system of WL | Delft Hydraulics for the aquatic environment. Modules for hydrodynamics, water
quality, ecology, waves and morphology are available to examine phenomena in coastal, river and
estuarine areas. For example, these systems are currently being used in a system for determining the
consequences of a calamitous discharge of pollutants, in a system that calculates the result of changes
in the forelands of a river and in a system that operates the large storm surge barriers in the
Netherlands.
The functionality of both modelling systems is constantly improved and extended as a result of many
consultancy and research projects and of the feedback from its users. Both SIMONA and Delft3D now
exist for more than ten years. Both systems have become large and, more importantly, rather complex.
This is due to the large variety of functionality within the modules and to the many cross-links
between the modules in the two systems. It has been observed that both systems are lacking flexibility
to further improve the systems. Consequently, the costs of further development and maintenance of
these modelling systems are rapidly increasing, and will soon grow beyond the capabilities of one
organisation. This is the reason why Dutch Rijkswaterstaat and WL | Delft Hydraulics decided to co-
operate in a public private partnership to develop a new modelling system, which is called the Open
Modelling System (OMS).
In August 2000 an agreement between the Dutch Rijkswaterstaat and WL | Delft Hydraulics was
signed in order to migrate from their current modelling systems, being SIMONA and Delft3D,
respectively, to one Dutch Open Modelling System (OMS). Top priorities in the migration phase are
integrity and stability of intermediate releases and transparency of the migration to users of both
systems. The aim of this integration is to be able to meet future demands of users with respect to
functionality, flexibility, accessibility, modularity and performance.
The new Open Modelling System should be open to new functionality and developments, which is not
the case in the present systems SIMONA and Delft3D-FLOW. Moreover, the OMS system should
allow connectivity to standard software packages, such as GIS, database software and software for pre-
and post-processing and visualisation. The use of industrial standards for communication and data
storage should guarantee a better access to commercially available software. Summarising, the OMS
system will lead to a more flexible environment, both from developers' and from users' points of view.
In February 2001 two projects have started to realise the OMS system, namely for:
As part of the second project C programming rules are defined to guarantee the reliability and stability
of the OMS modelling suit. The aim of these C Programming rules is to meet future demands of users
with respect to functionality, flexibility, accessibility, modularity and performance.
Contents
1 DECLARATIONS ........................................................................................................... 11
1.3 Constants.............................................................................................................................. 12
1.5 Macros.................................................................................................................................. 13
2 CONTROL STATEMENTS............................................................................................. 15
4.1 Varargs................................................................................................................................. 27
6.2 Language.............................................................................................................................. 35
6.8 Letters................................................................................................................................... 37
6.14 Indentation........................................................................................................................... 39
6.16 Fileheader............................................................................................................................. 39
7 RECOMMENDATIONS .................................................................................................. 42
9 REFERENCES............................................................................................................... 49
1 Declarations
1.1 Variables
Each variable must be declared on a separate line.
Each variable must be explained by means of a comment string. This comment string will be placed
directly after the declaration or definition and may be continued on one new line at the most.
Local variables must be declared at the beginning of a function and also at the beginning of a block.
All basic variables (int, float, char, long, short and double) are initialised at declaration. Don’t assume
the compiler will initialise variables at zero or blanks for you. Exceptions to this rule are static and
external variables, according to ANSI C they will always be initialised at zero. Mind the usage of
already defined functions.
Examples:
• It may be necessary to keep the state of the program in a globally available structure, because in
most user-interfaces the callback functions that are typically used to handle window events do not
allow an arbitrary argument list.
• It may be necessary to pass information on to higher-level routines, for instance error conditions
and there is no way (because of third-party libraries) to handle this information flow via an
argument that is passed by reference.
• When using such tools as Yacc and Lex, information is mainly transferred via global variables, as
there is no other way.
Therefore we need an etiquette for the use of global variables. This etiquette is given below:
The use of global variables is forbidden (with or without the static attribute), unless they are contained
in a structure. The definition of this structure is contained in an include file as a separate user-defined
type (typedef).
Any routine seeking to use this globally available structure, should obtain a pointer to this structure
either via its argument list or via a function call:
In some routine:
long routine( …, ProgramStatePtr statep, … )
{
…
/* Update the program state */
…
}
Or alternatively:
long routine( … )
{
ProgramStatePtr statep;
statep = GetProgramState();
The global variables within this structure can best be updated via get/set routines, rather than direct
assignments to the individual fields, as this makes the application less susceptible to changes in the
structure.
One advantage of using structures and pointers to these structures is that, should it become necessary
to distinguish several such items, one can simply allocate a new structure of this type and fill it.
1.3 Constants
The best way to define constants is to declare them with the const qualifier. This way you specify
that its value will not be changed. Using the const qualifier for an array it says that the elements will
not be altered of when used for array arguments it indicates that the function does not change that
array. An advantage is the compiler checking of the variables that have been declared with the const
qualifier.
Examples:
const long number = 42; /* Crucial number */
const double e = 2.71828182845905; /* Math’s exp(1) */
Caution:
The scope of some basic C-types is platform dependent or even compiler dependent. For that reason
porting an information system from one platform to another may cause adjustments to that information
system. This is hardly avoidable in those cases where software layers are very close to the ANSI
library. Therefore the usage of typedef’s is depreciated for the ‘lower’ software layers.
1.5 Macros
Use macros (#define) to define constants and only leave trivial constants (such as 0 and 1)
unchanged in the code. Preferably use a module prefix for a macro from a dedicated module.
Always use capitals when naming the macro and take care not to redefine a predefined macro like
BUFSIZ (defined in stdio.h) or M_PI (often defined in math.h)
Always use brackets in expressions in a macro. This will avoid an incorrect evaluation of the
expression.
Using a set of macro definitions where an enumerated type definition can be used is not allowed:
Note: Beware of side effects when using a macro, for instance when increments might be used in the
parameters to the macro.
The description of a #define constant or macro has to placed before the definition, for example
(otherwise the comment might become part of the macro’s value):
1.6 Prototypes
When using an (external) function the existence of a declaration of the so-called function-prototype is
required. It is an error if the definition of a function or any uses of it do not agree with its prototype.
An important reason for function prototyping is the improving software maintenance and type
checking by a standard-compliant compiler.
Function prototypes have to be declared in a header (*.h) file. The usage of header files with function
prototypes needed may only be achieved with an #include statement. To be sure the function
prototypes and function definitions are the same the header file with function prototypes must also be
included in the .c file with the function definitions.
Use the const keyword for function parameters that will not change and the keyword void for a
function that returns no value. This will explain something about the parameter as well as the function
and, moreover, the compiler can check for certain errors that violate these constraints.
If a function has no parameters, the keyword void (instead of empty parentheses) has to be used.
Leaving out void means the list of parameters can be anything – checking by the compiler is then
impossible.
Example:
void GiveMessage (
long state,
long message_type,
char * text );
Examples:
double * pWlevel; /* waterlevel */
long * pMmax; /* size M-direction */
char ** pstrings; /* Array of strings */
2 Control statements
2.1 General
Each language has its so-called idiom (cf. Kernighan and Pike, 1999). For C (and C-like languages,
such as C++ and Java) this includes:
• The way for-loops are normally set up
• The use of certain functions, especially the functions from the standard library
• The use of return values, such as 0 and 1
Knowing the idiom and using it is crucial: if you do not follow that style of programming, the readers
of your programs can very easily get confused and make mistakes that could have been avoided.
For instance, a function that is able to do its job will normally return zero to indicate this. So, using a
function that returns 1 instead to indicate everything was fine, goes against the grain. Similarly, it is
customary in C to use counters and indices that start at zero, rather than at 1. Use this and similar
conventions at all times. This chapter helps identifying the most common idiom and specifies several
additional constructions and keywords. All are meant to help creating robust and maintainable
programs.
A corollary to the above statements is: Use the standard functions whenever possible. This applies in
particular to string manipulation functions (such as strcmp()) and character type functions (such as
isalpha()). Besides the ones defined in the ISO C standard, we also define the following as part of the
OMS portability library:
• mkfilename() to construct the full path (directory and name) for a file from its parts
• strdup() to duplicate a string
2.2.1 For-loops
The for-loops have two common forms:
2.2.2 While-loops
The while-loop has three acceptable forms, whereas the do-while loop has only one:
while ( some_condition )
{
…
}
The reason to allow this construction is that it is accepted idiom (cf. Kernighan and Pike) and that
alternatives would require more logic or a slightly awkward sequencing (reading the line at the end of
the loop for instance).
2.2.5 Examples
Here are some examples of loops that are not allowed and their corrected alternatives:
• Multiple assignments in the various parts of the controlling part of the for-loop
for ( i = 0, j = 0;
i < n; j++ )
{
…
}
The reason for this is that is very easy to make mistakes that are hard to spot (see the second
example above).
Example:
case CASE3:
pstr = ”Case 3”;
printf( ”%s\n”, pstr );
break;
default :
fprintf( stderr,
”Impossible case!” );
break;
}
For example:
if ( strcmp( string1, string2 ) == 0 )
{
… /* First case */
}
else if ( strcmp( string1, string3 ) >= 0 )
{
… /* Second case */
}
else
{
… /* Default processing required! */
}
More specifically:
• Use the keyword “const” to indicate that arguments will not change. This enables the compiler to
check illegal or unexpected changes.
• Do not locally change arguments that have been passed by value, even though the changes will
have no effect in the calling routine. It is bad programming.
• Consider the difference between *arg and arg[]. The first can be used as a synonym for passing
by reference or for passing an array. The second explicitly states that an array is expected.
• All subroutines and functions must have a proper prototype.
• Use the ISO style for defining the arguments, not the older K&R style:
• Use an explicit return statement if the function is supposed to return a value. Do not use an
explicit return if it does not:
return retval;
}
• Check preconditions
Use the standard macro assert() to indicate crucial preconditions, both as a means of
documentation and to terminate the program, should there be no other solution (this requires the
use of the standard pre-processor macro NDEBUG).
• Use the special macros __FILE__ and __LINE__ to communicate where “impossible” conditions
occurred
Via the referred macros it is possible to automatically include the position in the source code
where an impossible condition occurred. This does not carry the same information as a full stack
trace, but it saves searching for the right place:
default : /* Should never occur */
fprintf( stderr, ”Line %s in %s: Impossible case!”, __LINE__, __FILE__ );
The advantage of these alternatives is that the compiler can produce better diagnostics if they are
used wrongly.
are not allowed, because there is virtually no advantage for these functions to ordinary functions,
and the compiler can not always check that the construction is correct. Debugging them is also
much more difficult.
• Creative use of the preprocessor, for example to avoid repetitive code, is allowed, provided this
type of use is well documented.
Practical examples of such use are:
• Interface definitions that have to take of platform-dependencies (the interfacing between Fortran
and C for instance)
• Set and get functions for the individual fields of large structures (see Appendix A):
#define set( ”name”, value) …
1
This is to some extent a matter of taste. In OO environments the emphasis is very much on such small
individual set/get routines, rather than a single pair that selects a field.
• Conditional preprocessor statements should not be used, unless to capture platform dependencies
Such statements may certainly not be used to:
• Hide currently unused (old?) code fragments
/* Old code - should be removed one of these days */
#if 0
x = y*y;
#endif
• Select or deselect debugging statements
/* Debugging code - needs some reworking though */
#if DEBUGON
fprintf( stderr, ”x = %f\n”, x );
#endif
The reason is that such code would not get compiled and would not evolve with the rest. Hence
after a cycle or two of maintenance the code is outdated and it probably will not compile correctly.
A better solution in the context of OMS is to use an if-statement with a more or less trivial
condition:2
if ( debug_on )
{
… /* Debugging */
}
2
An alternative that does use a macro, is presented in Chapter 7.
3 Using I/O
The use of external files in C has very similar problems as in Fortran or Java: the files may or may not
exist, reading may or may not cause run-time errors and so on. Yet the details of these problems differs
from the other programming languages and this chapter means to help avoid them by imposing a
number of rules.
The example below shows what happens if you go against some of these rules:
if ( infile != NULL )
{
fscanf( infile, "%s", string ); /* %s reads a single word */
fscanf( infile, "%f", &rvalue ); /* %f reads the next word! */
fscanf( infile, "%d", &ivalue ); /* %d reads the third word */
rvalue = 1.0e20;
printf( "Big real: %f\n", rvalue ); /* %f uses as many positions as necessary*/
fclose(infile);
}
return 0;
}
if ( infile != NULL )
{
fgets( buffer, sizeof(buffer), infile );
sscanf( buffer, "%s", string );
4.1 Varargs
Modules with a varying number of parameters must be avoided as much as possible. Only if there is
no other solution, are they allowed and then the use of varargs is required.
The constants TRUE and FALSE may only be used in assignments or in return value. The usage as
relational operand is not allowed.
4.3 Parentheses
For readability reasons usage of parentheses in expressions is strongly recommended. On the other
hand using too many brackets can undo the readability. So handling with due care is needed.
In this example the index i may not be increased if c[i] turns out not to be a space, as the second part
of the logical expression will not be evaluated!
The conditional expression may seem very compact, but as soon as the expressions become more
complicated, it is very difficult to see what is going on.
The reason that these examples are allowed is that they make it possible to abbreviate constructs
without obscuring the actual intent. In the first example the use of a macro allows one to hide the
complicated structure of the conditional expression. In the second example there is no need for an
extra if-statement and a variable to hold the strings “true” and “false”.3
3
Actually, even in these cases other solutions may be preferable. See Chapter 7.
4.9 General
To avoid common errors and uncertainties, some general requirements about statements and
expressions are formulated:
• Do not use the increment (++) and decrement (--) operators in one single expression;
• Usage of the comma-operator is forbidden;
• On one line only one single assignment is allowed. Both operators ‘++’ and ‘- -‘ also are
assignments;
• An actual argument may not contain an assignment. Actual arguments are arguments of the
statements if, while, switch, return, do and for. Only one exception is allowed:
the while expression may contain one single assignment.
For example:
while ( (c = fgetc(infile)) != ‘\n’ )
• Using identical names for different variables in different blocks in the same function is not
allowed.
Or, alternatively:
char * IO_GetNewName ( void )
{
static char nameBuffer[80];
…
return nameBuffer;
}
Pointers to local non-static variables are valid only as long as the routine in which the variables are
defined is active. Upon return the memory is reclaimed and such pointers become invalid.
Note that real constants in C get the type double, unless they are suffixed with “f”. Integer constants
have the type int, unless they are suffixed with “L”:
Constant Type
4 int
4L long
4.0 double
4.0f float
Pointers are often compared with NULL to determine the end of a data structure or test for validity. In
this case the comparison has to be explicit!
5 File organisation
Source files and include files must be organised in a standard manner, to increase readability and
maintainability of the source and include (header) files.
*/
#include <stdio.h>
#include <stdlib.h>
#include ”dictionary.h”
/* Macros:
INITIAL_SIZE - initial size for a dictionary
INCREMENT - amount by which to increment the array
*/
#define INITIAL_SIZE 20
#define INCREMENT 10
/* Data types:
Cursor, CursorPtr - used to keep track of search
*/
typedef struct _Cursor
{
...
} Cursor, * CursorPtr;
4
There is a subtle difference between these two types of include statements. The triangular brackets are
reserved for files in the standard include directories.
/* Static functions
*/
static int
_DictIndex(
DictionaryPtr dict,
char * keyword );
/* Global variables:
cursor - Cursor into the given dictionary
*/
static Cursor cursor;
static long
_DictIndex(
DictionaryPtr dict,
char * keyword )
{
... /* Body of routine */
}
char *
DictGetValue(
DictionaryPtr dict,
char * keyword )
{
... /* Body of routine */
}
To prevent problems with multiple inclusions the whole content is enclosed by an #ifndef
construction:
#ifndef FILENAME_H_INCLUDED
#define FILENAME_H_INCLUDED
#endif /* FILENAME_H_INCLUDED */
Note:
The macro used to indicate this inclusion always has the above form (replace FILENAME by the
actual name in capitals).
Here is an example:
/* dictionary.h - Header file for dictionary routines
*/
#ifndef DICTIONARY_H_INCLUDED
#define DICTIONARY_H_INCLUDED
#include <stdio.h>
#include <stdlib.h>
/* Macros:
NOT_FOUND - return value to indicate the keyword was not found
*/
#define NOT_FOUND (char *)NULL
/* Data types:
Dictionary, DictionaryPtr - the structure to hold a dictionary
*/
typedef struct _Dictionary
{
...
} Dictionary, * DictionaryPtr;
/* Global variables:
None
*/
/* Public functions
*/
char *
DictGetValue(
DictionaryPtr dict,
char * keyword );
#endif /* DICTIONARY_H_INCLUDED */
See the recommendations for further remarks about names for routines and functions and the names of
source and include files.
This chapter is limited to the layout of and the documentation within the program text.
• The names have to be consistent, in other words: for the same meaning the same name has to be
used. (For the use of acronyms see chapter 7 “Recommendations”.)
6.2 Language
All comments and other documentation have to be written in English without any exceptions.
The strict order of program text parts of a header file (.h) is:
• file header comment block with:
• file name
• programmer name
• version number, date and description
• copyright
• file description
• includes
• standard files
• project files
• defines
• constants
• macro's
• enums and typedefs
• enums
• typedefs
• external variable declarations
• external function prototypes
The required order of program text parts of a program file (.c) is:
• file comment header block with
• file name
• programmer name
• version number, date and description.
• copyright
• file description
• keywords
• includes
• standard files
• project files
• defines
• constants
• macros
• enums and typedefs
• enums
• typedefs
• external variable definitions
(are declared in header files)
• internal variable definitions
( static variables)
• static function prototypes
(external function prototypes are placed in header files)
• function definitions (global and local)
• for every function:
• function comment header block with:
• function name
• function description
• summary
• function heading (type, name, parameterlist)
• function body
6.4 Comment
To explain the functioning of a program, the program text has to be clear enough. This can be
achieved by adding comment text to the program text. The language of the comments is always
English.
Comment headers (the first line of a comment block) start with a slash (‘/’) at the first position
followed by between 40 and 80 asterisks (‘*’). Every continuing line (except the last line) starts with
an asterisk on the first position. The last line starts with between 40 and 80 asterisks, followed with a
slash.
Comments consisting of only one single line must be indented according to the surrounding program
text. Comments of more than one line may start at the left margin in order to make use of the full line.
Global comment preferably is placed before the function itself and detailed comment within the
function. Detailed comment blocks of more than 10 lines are preferably placed before the function or
else directly at the beginning of the function. The reason for this is to prevent large pieces of comment
statements that decrease the readability of the actual program text.
Short comment (comment behind program statements) may only be used in case the readability not
decreases.
Example:
long cntr;
char * string;
char opt;
char ** pString;
6.6 Statements
Only one statement per line is allowed. Statements should be consistently indented according to the
extended style:
if ( a > b )
{
z = a;
y = b;
}
In order to clarify the sequence of evaluation of sub-expressions brackets must be used consistently.
Examples:
q2r = d / 2.0;
area = M_PI * (r * r); /* M_PI is used from math.h */
c = sqrt( a*a + b*b );
V0 = j0(M_E/t1) * (t1 – t0); /* M_E is used from math.h */
6.8 Letters
Small letters as well as capitals may be used in program text and comment lines.
Note:
Besides the above mentioned limits the requirements of readability, easy reference, being structured,
modularity and cohesion of functions stay into effect.
6.12 Spaces
Spaces should be used to increase readability.
Notes:
• Don’t exaggerate the use of spaces. In the last example, the spaces around ‘v’ and ‘w’ are left out
for readability reasons.
6.13 Braces
Braces have to be used in combination with the if, for, while and do statement, even if only
one command is involved.
Both braces have to be placed straight above each other and directly beneath the related statement.
Example:
if (a > b)
{
a = b;
}
6.14 Indentation
From a readability point of view it is important to have a clear structure of the program text. This
structure must be achieved by indentations in case of nesting. Nesting one single level must be
accompanied by an indentation of 3 spaces. Indentations may not be achieved by using tab characters.
Also use indentation in case of a function call which consists of more than one line.
6.16 Fileheader
All C program files (.c, .h files) need to have a so-called file header block. For some simple examples
also see the paragraph “Examples”.
These descriptions need to be in the top of the C-file in the same order.
These descriptions need to be in the top of the h-file in the same order.
Functions
When defining the function a description of the return value has to be given after the function heading
(type and name).
Example
long
ifac ( /* return value: faculty of integer number */
long inum) /* I integer number */
...
}
parameters
All above-mentioned descriptions must be on the same line at the right of the definition.
6.19 Variable/constant
Like parameters, after each definition of variable or constant the meaning has to be described.
Because of the fixed format of these version lines an automatic check can be done. The check consists
among other things of correct increase of the version number or a check whether a (proper) problem or
project identification is given.
7 Recommendations
Portability library:
• Define a function to compose directory and file name (mkfilename())
• Define a function strdup() if necessary
• Define wrappers for common routines like fopen() that are bound to require checking.
• Define a PATH_MAX macro, if not defined in the standard include file “limits.h”
• Define TRUE and FALSE as respectively 1 and 0. Check that these values are used if already
defined
• Define UNDEFINED_NAME as “--Undefined--”
• Define a set of routines for logging error messages
• Define a macro INVALID_STATE that prints a mesage to stderr about the program being in an
impossible or invalid state.
• Define a macro ON_DEBUG like this:
#define ON_DEBUG( level, code ) \
if ( _DebugLevel( level ) { code }
(This macro uses a function _DebugLevel to decide if debug output is required (given the level),
and if it is, then execute the given code. It is a flexible alternative to the code fragment presented
at the end of section 2.7
• Define macros MAX() and MIN() (functions are not possible because we would need to
distinguish between types)
• Define a function BooleanString() that returns “true” or “false” given the value of its (boolean)
argument. This makes the ?: construct in section 4.5 superfluous.
Data types:
• Restrict the use of unsigned integers
• Always use long integer (at least: do not try to be smart about short/int/long to save a few bytes)
• If you have to use << and >>, always on unsigned variables (only allowed when constructing
bitmasks?)
• Restrict the use of unions, hardly ever necessary.
• Bitmasks should only be used “symbolically”
• Know when to use size_t and sizeof()
Logical expressions:
• Use 0 == x instead of x == 0 (if == is typed as =, the first gives a compiler error!)
The advantages of this approach are that changes in an include file only affect the source files that
really use the routines and functions it declares. The disadvantage is that the list of include files in the
top part of each source file can become very long and unmanageable. Also the number of files to
maintain is twice the number of source files. This is especially annoying if the routines are rearranged
in other source files. What is more, the programmer will have to know which file defines which
routine.
The advantages are that the list of include files will be short. The total number of files will be only
slightly larger than the number of source files. The disadvantage is that changes in prototypes for
routines found in a.c for instance, also affect files that include abcd.h but do not use these routines.
Our recommendation is the latter (see also chapter 10), as this seems more manageable and allows
header files for libraries rather than for individual source files. That is, define one header file per
library.
The names of routines and functions must start with a capital if they are publicly useable, otherwise
they should have a name starting with a single underscore and then the same prefix. (Further
refinements are possible but not desirable).
For example:
The routines and function that manipulate a general hash table structure are found in two source files,
hash1.c and hash2.c, because of the total size of the routines. Then the following prefixes are
available:
• Hash Any publicly useful routine or function
• _Hash Private (both static and non-static) routines in hash1.c or hash2.c.
These routines may also be used in the other source file but are not meant for use beyond the hash
module. Via the underscore it is indicated that the non-static routines are not meant for use outside this
collection.
In all cases:
The names of routines and functions must indicate their proper use. Therefore:
• Use verbs that indicate the action
• Use forms like “Is” or “Has” to indicate that something is available or of the mentioned type.
Miscellaneous
signal() The semantics of signals is very difficult. Their use is in the majority of
cases not necessary anyway.
setjmp()/longjmp() These routines cause a non-local jump and therefore lead to
unmanageable program control flows.
abort() When aborting the program, files are not properly closed
atexit() Registering call-backs is an unnecessary complication in most programs
atabort() Ditto
This horror has been illustrated below (note that the hidden variable “listp” becomes visible whenever
you want to do something with the elements of the list):
A trivial example that attempts to introduce extra keywords (Pascal, Fortran, …) into C is this:
/* DO NOT USE THE PREPROCESSOR IN THIS WAY */
#define BEGIN {
#define END }
if ( x > 0 ) BEGIN
/* Do something */
END
A similarly forbidden construct is this “function” that may be used to define debugging statements:
/* DO NOT USE THE PREPROCESSOR IN THIS WAY */
#define ON_DEBUG( a ) if ( _debug_ ) { a }
The alternative here is the same as above: use a proper if-construct. The code will always be
compiled, yet there is little or no impact on the performance if it is not used, because the controlling
parameter is false.
Note:
The only exception to the rule of not hiding code constructions in macros is the standard assert()
macro. Using this macro is actually encouraged, to document the preconditions and post-conditions of
a function.
The problem is that strcmp() return 0, -1 or 1, so not a logical value at all and that the check is
supposed to be “is string1 equal to string2? If not, return an error”. Because strcmp() returns a zero
(ordinarily a false value) when the two strings are equal, the form of the above fragment is as if the
strings have to be equal for the error message to be printed. This is confusing.
Another example:
9 References
Les Hatton (1995)
Safer C, Developing Software for Hingh-intergrity and Safety-critical Systems
McGraw-Hill, 1995
By defining a large number of individual get and set routines, this can be solved in a straightforward
manner. However:
There were at least 30 fields involved, so this solution would require 60 individual set/get functions.
If the underlying structure changed, one would need to create new get and set functions or update the
existing ones.
Therefore a solution was sought and found in the following way (details have been left to clarify the
solution):
A generic set of macros was defined, one for each type of data items
The macros were embedded in a routine that could be called from Fortran where one of the arguments
is the name of the field (so a string!).
From the Fortran side, one can set a field “noparams” as:
call setpar( ’noparams’, noparams )
On the C side the code that is executed looks like this (*value because Fortran passes everything by
reference):
void setpar( char *field_name, long *value )
{
…
if ( strcmp( field_name, ”noparams” ) == 0 )
{
data->noparams = *value;
}
…
}
The if-construction is repeated for all relevant fields, but actually this is done via the following macro:
#define INT_VALUE( field ) \
if ( strcmp( field_name, #field ) == 0 ) \
{ \
data->field = *value; \
}
So, the C routine “setpar” can actually look like this:
void setpar( char *field_name, long *value )
{
…
INT_VALUE( noparams )
INT_VALUE( intopt )
INT_VALUE( nosubst )
…
}
Appendix C Examples
/********************************************************************
*
* Filename owndef.c
* Programmer H.Hanzon
* Version 1.00 27-11-1994 First Edition
*
* COPYRIGHT
*
* Copyright (c) 1994 "Rijkswaterstaat"
* Permission to copy or distribute this software or documentation
* in hard copy or soft copy granted only by written license
* obtained from "Rijkswaterstaat".
* All rights reserved. No part of this publication may be
* reproduced, stored in a retrieval system (e.g., in memory, disk,
* or core) or be transmitted by any means, electronic, mechanical,
* photocopy, recording, or otherwise, without written permission
* from the publisher.
*
*********************************************************************
*
* FILE DESCRIPTION
*
* This file (module) contains functions to manage the traffic at a
* crossing.
*
* Warning!
* This example is for clarification purposes only.
* Each definition and declaration stands for itself.
* Together, they do not form a coherent program structure.
*
*********************************************************************
#include <stdio.h>
#include <math.h>
#include "owndef.h"
/* traffic lights */
#define RED 1 /* red light */
#define ORANGE 2 /* orange light */
#define GREEN 3 /* green light */
#define BLINKING_ORANGE 10 /* warning light */
enum hallo
{
Here,
There
}; /* enumeration (example) */
/********************************************************************
*
* FUNCTION NAME GiveMessage
*
* FUNCTION DESCRIPTION
*
* This function produces a message, depending on the state of the traffic
* light.
*
* Warning: the description of the function may be long, but it should always
* start with (at most) two lines with a summary of the full description.
* These two lines are preceded and followed by at least one blank line.
*
*********************************************************************
*
* PSEUDO CODE
*
* if the light is red then
* print "stop"
* elseif the light is orange then
* print "go or halt"
* elseif the light is green
* print "go"
* else
* print "watch out"
*
********************************************************************/
switch (lState)
{
case RED: /* the light is red */
printf ("stop\n");
break;
/********************************************************************
*
* FUNCTION NAME ChangeState
*
* FUNCTION DESCRIPTION
*
* This function changes the state of the light, depending on the incoming
* (current) state.
*
*********************************************************************
*
* PSEUDO CODE
*
* if the light is red then
* change light to green
switch (*plState)
{
case RED: /* make it green */
*plState = GREEN;
break;
if (*plState == BLINKING_ORANGE)
{
iReturnValue = 1; /* error */
}
else
{
iReturnValue = 0; /* ok */
}
return iReturnValue;
}
/********************************************************************
*
* Filename h_exampl.h
* Programmer A.L. Weg
* Version 1.00 27-11-1994 New file
*
* COPYRIGHT
*
* Copyright (c) 1994 "Rijkswaterstaat"
* Permission to copy or distribute this software or documentation
* in hard copy or soft copy granted only by written license
* obtained from "Rijkswaterstaat".
* All rights reserved. No part of this publication may be
* reproduced, stored in a retrieval system (e.g., in memory, disk,
* or core) or be transmitted by any means, electronic, mechanical,
* photocopy, recording, or otherwise, without written permission
* from the publisher.
*
*********************************************************************
*
* FILE DESCRIPTION
*
* This is my own header file
*
********************************************************************/
#include <stdio.h>
#include "special.h"
enum peer
{
to,
peer
}; /* enumeration example */