P99 implements many different features through macros and functions, too many to mention explicitly in such an overview. You will find a structured hierarchy of descriptions below the "Modules" tag and the documentation of individual items under "Files" -> "Globals". Here we will introduce some main features:
In section Use of temporary lvalues we saw a way to provide default arguments to functions by overloading them with macros. The general declaration pattern here is as follows
NAME becomes the name of a macro and where we also suppose that there is already a function of the same name
The default value for the ::hostname macro above was produced by a macro, namely
hostname_defarg_0. The evaluation of the default value is done in the context of the call and not in the context of the declaration. For default arguments that are not constants but expressions that have to be evaluated this is a major difference to C++. There, default arguments are always evaluated in the context of the declaration.
The convention here is simple:
NAMEwith the token
defargand the decimal number
As we have seen in the example (a) is computed in the context of the caller. This let us simply use a temporary (here a local compound literal) that was thus valid in that context.
To obtain the same behavior as for C++, namely to provide a default argument that is evaluated at the place of declaration and not at the place of the call we have to use (b), a function call. This will be as efficient as a macro call if we use Inline functions for that purpose.
To ease the programming of this functional approach, P99 provides some machinery. We need three things as in the following example:
Here in the example, a default argument is provided for positions 1 to 3 but not for position 0. All three defaults have the type
unsigned short. The above code leads to the automatic generation of three
inline functions that look something like:
This declaration and definition is placed in the context of the above declaration and not in the context of the caller. Thus the expression is evaluated in that context, and not in the context of the caller. In particular for the third function, this fixes the variable
rand48_counter to the one that is visible at the point of declaration.
Resource management can be tedious in C. E.g to protect a critical block from simultaneous execution in a threaded environment you'd have to place a lock / unlock pair before and after that block:
This is error prone as locking calls must be provided for each critical block. If the block is longer than a few lines it becomes increasingly difficult to ensure the unlocking of the resource, since the lock / unlock calls are spread at the same level as other code.
Within C99 (and equally in C++, BTW) it is possible to extend the language in order to make this more easily visible and to guarantee that lock / unlock calls match. Below, we will give an example of a macro that will help us to write something like
To make this even more comfortable we have
There is an equivalent block protection that uses an atomic_flag as a spin lock. Such a spin lock uses only atomic operations and can be much more efficient than protection through a ::mtx_t, if the code inside the critical section is really small and fast:
For cases where the atomic_flag variable would be specific to the block, you don't even have to define it yourself:
Generally there should be no run-time performance cost for using such a macro. Any decent compiler will detect that the dependent code is executed exactly once, and thus optimize out all the control that has to do with our specific implementation of theses blocks.
Other such block macros that can be implemented with such a technique are:
An even more sophisticated tool for scope-bound resource management is provided by the macro P99_UNWIND_PROTECT
P99_UNWIND_RETURN goes one step further. As for P99_UNWIND, it executes the clause after P99_PROTECT, but when it reaches the end of the P99_UNWIND_PROTECT scope it will return to the caller with a return value as specified after P99_UNWIND_RETURN, here the value
We provide some utilities to ease the programming of loop iterations in one or multiple dimensions. The simplest to use is P99_DO, that closely resembles a
do loop in Fortran. It fixes the bounds of the iteration once, before entering the iteration itself.
P99_FORALL allows the generatation of nested for-loops over an arbitrary number of dimensions:
will iterate over all combinations of
k in the bounds specified by
P99_PARALLEL_FOR, where available, will provide a parallelized version of a simple
for-loop, and P99_PARALLEL_DO and P99_PARALLEL_FORALL implement nested parallel loops with otherwise the same semantics as for P99_DO or P99_FORALL, respectively.
P99 provides you with macro features that can become handy if you have to generate code repetition that might later be subject to changes. As examples suppose that you'd have to code something like
If, over time, there are many additions and removals to these lists, maintaining such code will not really be a pleasure. In P99 you may write equivalent statements and declarations just as
There are a handful of such predefined macros that you may look up under Produce C99 statements or expression lists. Under the hood they all use a more general macro that you may yourself use to define your own macros: P99_FOR. The use of this will be described in more detail under Macro programming with P99.
The predefined macros from above are also able to avoid the nasty special case that the variadic part of the argument list is empty. Something like
would at least cause a warning with conforming preprocessors if the macros were implemented directly with something like
since the variable length part should not be empty, according to the standard. With P99 you don't have these sort of problems, the above should just result in empty statements or declarations, that are even capable of swallowing the then superfluous semicolon at the end.
P99 avoids this by testing for the length of the argument list as a whole with P99_NARG and by using a macro conditional controlled by that length. Such conditionals like P99_IF_EMPTY ensure that the preprocessor decides which of two different code variants the compiler will see. The fragment
will expand to either
general_version according to
BLA. If it expands to an empty token, the first variant is produced, if there is at least one non-empty token the second version results.
P99 also implements logical and arithmetic operations in the preprocessor. Logical operations just evaluate to the tokens
1. Arithmetic is restricted to small decimal numbers, less than P99_MAX_NUMBER. Some examples
See Preprocessor operations for more about that.
Consistent initialization of variables is an important issue in C. P99 provides some tools to help with that, most importantly a macro P99_NEW. Therefore we have to relay on some assumptions that are specified in Variable initialization, in particular that there is an `init' function for each type that we want to use with P99_NEW.
For the example type of a circular list element
we might want to ensure that the fields
succ are always properly initialized. An `init' function could be as follows:
A static initialization of a 4-element list in file scope can then be done as
Dynamic initialization of a 4-element list on the stack in function scope
For dynamic initialization we would then define something like this:
Initializations of this type of heap variables in function scope can now simply look like this
These define cyclic lists of 3 elements, well initialized and ready to go.
In fact, the P99_NEW macro takes a list of arguments that may be arbitrarily 3 long. It just needs the first, which must be the type of the object that is to be created. The others are then passed as supplementary arguments to the `init' function, here the parameter
The new C standard C11 (published in December 2011) introduces some new features that are already present in many compilers or OS, but sometimes with different syntax or interfaces. We provide interfaces to some of them with the intention that once compilers that implement C11 come out these interfaces can directly relate to the C11 feature.
With these emulated interfaces you can already program almost as if you had a native C11 compiler (which doesn't yet exist) and take advantage of the improvements that C11 makes to the language, without giving up on portability in the real world of today's compilers.
C11 provides a new feature to "overload" macros and more generally the result of any type of expression,
_Generic. It allows to write template-like expressions with the macro preprocessor. The idea is to generate type generic mathematical function that already had been present in C99:
If you include the "tgmath.h" header you have a macro
sin that implements calls to the family of sine functions, e.g
At compile, these type generic macros decide from the type of the argument which function call to emit.
The new concept of
_Generic expressions generalizes this concept. From the usually public domain compilers at the time of this writing (Apr 2012) only
clang implements this feature already. On the other hand
gcc has extension that can be used to emulate it, and such an emulation is provided through P99_GENERIC.
Atomic operations are an important contribution of the new standard; these operations are implemented on all commodity CPU nowadays but a direct interface in a higher programming language was missing.
These operations give guarantees on the coherence of data accesses and other primitive operations, even in presence of races. Such races may occur between different threads (see below) of the same application or when a program execution is interrupted, e.g for a signal handler or a
longjmp call. Since most instructions on modern CPU are composed of several micro-instructions, in such a context an instruction may only succeed partially and a data may end up in a intermediate state.
In this example
n is always in a clean state: either the addition of
has taken place or it has not. Multiple threads can execute this code without locking a mutex or so, the value of
n will always be well defined.
One of the interesting concepts that come with C11 is atomic_flag, that is a simple interface that can implement spinlocks quite efficiently.
Atomic operations have their major advantage in the presence of threads, that is multiple entities that compute concurrently inside the same application and using a common address space. C11 provides an optional interface for threads and the principal data structures that are needed for them (::thrd_mutex_t and ::thrd_cond_t). This thread interface is a bit simpler than POSIX threads, but implements the main features.
P99 provides a shallow wrapper on top of POSIX threads that provides all the interfaces that are required by C11.