P99
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
C99 features

As extensions to C89, C99 offers some features that improve the possibilities of programming efficiently and portably at the same time. There are four of these new concepts that are particularly important for P99 and without them P99 wouldn't be possible.

Variadic macros

The preprocessor now has a feature that previously only C functions had: a macro may accept an argument list that may vary in size. As with functions, such a macro is defined with ‘...’ in the argument list to indicate a list following the initial, named, arguments:

#define TOTO(NAME, ...) NAME[__VA_ARGS__]

The variable-length list is then referred to using the reserved identifier VA_ARGS.

This functionality of C99 allows us e.g to implement macros for Default arguments to functions and to perform Code Unrolling.

Inline functions

The new keyword inline is borrowed from C++ with some slightly changed semantics. The important part for P99 is that functions can be defined in a header file (and not only declared) when specified as inline.

This allows us to define small wrappers in header files using macros, without generating conflicts in different compilation units. By that we can avoid one of the major drawbacks of C macro programming: a macro cannot define another macro. In addition functions, when compared to macros, have other advantages

  • they are typesafe
  • their arguments are evaluated exactly once

Designated initializers

In C89, initialization of structures can be tedious and error prone:

typedef struct toto toto;
struct toto {
unsigned a;
double b;
};
.
.
toto A = { 0, 1 };

Components are initialized in the order of the type declaration. Here the 0 in the initializer is used to initialize the component A.a and the 1 for A.b.

Whenever the structure toto changes during the development process, we would have to revisit all initializations to see whether or not they remain consistent:

  • if the order of components a and b changes, the order of the expressions must be inverted
  • if we insert an element before a or b, the initialization of b by 1 in the example is replaced by the default initialization, namely 0.0.

Keeping track of these may be particularly difficult if the components are of similar types, such that an initializer for one is valid for the other.

With designated initializers this situation changes substantially:

toto A = { .a = 0, .b = 1 };

By this means we avoid all of the problems mentioned above. This scheme is robust against reordering and insertion of components. In a certain sense it is also robust against the renaming of components: all initializations will then simply fail at compile time, so it is easy to identify problems.

For a more detailed discussion of initialization and P99 see Variable initialization.

Compound literals

A compound literal is syntactically given as a compound initializer and a cast such as

(int[2]){ 4, 5}
(T){ .d = 1, .a = 10 }.

It is best seen as defining a temporary object of the requested type, initialized using the same rules that apply to a named variable of that type.

  • Such unnamed temporary objects can be initialized on the fly, e.g as the arguments to functions, and they live until the end of the block in which they are defined.
  • They define an lvalue from which an address can be taken.
  • Unless the type of the cast is defined with ‘const’ the content of such a variable is modifiable.

Example: The following code returns the pointer to a character array that is initialized with all ‘a’ and a terminating 0 character. The array is a valid object until the program leaves the current block.

char const*const hui = memset((char[256]){0}, 'a', 255);

It would be equivalent to the following

char tmp[256] = { 0 };
char const*const hui = memset(tmp, 'a', 255);

Using the compound literal here has the advantage that no other non-const reference to the temporary is exposed.

The compound literal syntax is not always very easy to read; in fact it might even hurt your eyes. P99 gives you a shortcut for compound literals that are initialized from the all 0 initializer. With that the above could have been written:

char const*const hui = memset(P99_LVAL(char[256]), 'a', 255);

Macros that hide a function

Per se, this is not a new feature of C99 but had been present before. The preprocessor has two special rules, one that applies generally to macros and the other that applies only to functional macros:

  1. If during expansion of a macro XXX the token XXX is found, it is not expanded. So there is no recursion in C macros.
  2. If a functional macro YYY is found without a following opening parenthesis it is not expanded.

Theses features can be used to define a macro and another identifier that have the same name. It is sometimes used for a test if some functionality is present on a platform. E.g on my computer I have

#define stdin stdin

This can be used as follows

#ifdef stdin
// Do something for a hosted environment
// Use stdin as usual
#else
// Do something for a free standing environment
// We don't have stdin at all, write to a log file or so.
#endif

But we may equally use this technique for a function symbol. POSIX explicitly allows this for example for the functions in stdio.h

The following shall be declared as functions and may also be defined as macros.
Function prototypes shall be provided.

Lets have a look at a randomly selected function from stdio and suppose it would be given as follows:

int putc(int, FILE *);
#define putc(C, F) (is_it_special(C) ? do_something_clever(C, F) : putc(C, F) )

(Yes this evaluates C twice.) With that, these uses of putc are still valid:

// Use the macro and implicitly the function, relies on rule 1
putc('A', stdout);
// Just use the function not the macro, relies on rule 2
(putc)('A', stdout);
// Get the address of putc and store it in my_putc, relies on rule 2
int (*my_putc)(int, FILE*) = &putc;

The example above with putc has a particular pitfall if we have the above definitions in a header file and then include this file at the place where we define the function:

#include <stdio.h>
int putc(int, FILE *) {
// do the right thing here
}

This will simply explode since the preprocessor will expand the functional reference to putc. This can be explicitly avoided by undefining the macro before the definition, but for this the implementor of putc has to know that it is also a macro.

With P99, we use this technique to overload a function to provide it with Default arguments to functions. A macro defined in that way will avoid this pitfall: if it is called with the same number of arguments (or more) that are all non-empty, it will produce the same token sequence as if the macro had not been defined.

Pragmas inside macros

The traditional approach in C had been to specify meta information for the compiler in so called pragmas:

#pragma omp parallel for
for (size_t i = 0; i < n; ++i) c[i] += a[i] * b[i];

The inconvenience of such a construct is that it has always to be on a line of its own and cannot be placed in a macro. For that reason most compilers provided extensions that let the programmer place meta information more precisely at some specific point of the code, e.g gcc has an attribute extension for that.

C99 adds a keyword to overcome that difficulty and to normalize the link between macros and #pragma: _Pragma.

_Pragma("omp parallel for") for (size_t i = 0; i < n; ++i) c[i] += a[i] * b[i];

P99 uses this feature for extensions concerning OpenMP, in particular the P99_PARALLEL_FOR and P99_PARALLEL_FORALL macros.

P99_LVAL
#define P99_LVAL(...)
Define an lvalue of type T, where T is the first parameter in the variable parameter list.
Definition: p99_int.h:1084
for
for(_Bool p00=1;p00 &&({ { __VA_ARGS__ } 1;});p00=0) typedef enum p00_uncase_enum
Prefer the statements in the argument list over the statement or block that follows.
Definition: p99_block.h:327
i
P00_CLAUSE2 i(_Pragma("weak p00_getopt_comp"))(_Pragma("weak p00_getopt_comp