|
P99
|
P99 uses some programming conventions that might be interesting for projects that include its header files.
Where we can, we try to conform to the C99 standard and to mark extensions clearly, if we use them.
The C specification has many places where it explicitly says that under certain circumstances the behavior of the resulting code is undefined. Generally this means that a conforming C implementation is not obliged to capture such circumstances and for code that uses such undefined behavior might do anything, from do-the-right-thing or crashing to eating your hard drive.
P99 should not produce any such undefined behavior.
In other places the standard leaves room for C implementations to specify certain behavior.
P99 tries not use any special feature that might be the result of such implementation specific behavior. This concerns in particular arithmetic on integer types. Here the standard allows certain variations:
sizeof expressions for shifts.int a we would use -(unsigned)a. This expression guarantees that the result is well defined even for corner cases (here a being INT_MIN in two's complement representation) and will never trigger a range error.typedefs uintptr_t or intptr_t since they are optional in C. In particular we may not assume that there is any sensible conversion between pointers and integer types.Macro names that implement the functionality of P99 are generally uppercase. Exceptions from that rule are Macros that hide a function. All other identifiers are lowercase.
P99 uses the common prefixes P99_ and p99_ for macros and other identifiers, respectively. Future P99 versions could define new identifiers with these prefixes. If you include any of the P99 files, avoid using these prefixes for your own identifiers.
The same rule holds for the prefixes P00_and p00_ which are used for auxilliary identifiers that need not be documented. Such identifiers are ignored in the doxygen documentation.
The P99 macros and functions as such should be independent of the execution system and compiler. Nevertheless, for the time being they are only tested on POSIX systems, namely Linux. So if problems are discovered with other systems, please let us know.
In contrast to that general policy, there is one file that is dependent on the system, p99_posix_default.h. As the name indicates it is designed for POSIX systems and provides default arguments for some POSIX functions.
Also, some of the examples throughout this documentation are taken from programs that would typically run on POSIX systems. We hope that such examples are obvious and don't need explanation for programmers of other systems.
Where possible, P99 uses initializers to initialize variables. For each type T where such an initialization is possible, there should be a macro T_INITIALIZER that does a standard initialization. Such a macro should use the Designated initializers scheme.
typedef struct toto toto; struct toto { double a; unsigned b; }; #define TOTO_INITIALIZER { .a = 0.0; .b = 0u }
In case you want the default behavior of C, namely that all fields are recursively initialized with 0 then you could just use
#define TOTO_INITIALIZER P99_INIT
to make this choice explicit.
Such initializers can easily be assembled together
typedef struct tutu tutu; struct tutu { toto A; bool c; }; #define TUTU_INITIALIZER(VAL) { .A = TOTO_INITIALIZER, .c = (VAL) }
As you can see in this example, INITIALIZER can be a `normal' macro or a function like macro.
For dynamic initialization we assume that an `init' function exists that
Often when programming utilities for C that are supposed to return a pointer to an array or structure, the question of who is allocating the space arises: the caller or the callee.
P99 goes a different way, in that it tries to remove most of the burden from the programmer of both caller and callee. Let us look at the hypothetical function
char const* hostname(char buffer[], size_t len);
which could be defined as being similar to the POSIX gethostname function, only that it doesn't return an error indicator but a pointer to the name or a null pointer if it fails. An old time (and dangerous!) calling convention for such a function would perhaps have been to return a statically allocated buffer in case that the buffer argument is a null pointer.
P99 lets you define more convenient and less dangerous calling conventions: Default arguments to functions allows us to define a macro of the same name that uses a compound litteral if no argument is given to the same function.
#define hostname(...) P99_CALL_DEFARG(hostname, 2, __VA_ARGS__) #define hostname_defarg_0() P99_LVAL(char[HOSTNAME_MAX]) #define hostname_defarg_1() HOST_NAME_MAX
This defines three different macros. One that is used where the programmer places a call to hostname. The other two, hostname_defarg_0 and hostname_defarg_1, are used by the macro hostname when the respective arguments are left out.
Now hostname can be used in three different ways.
char const*const host = hostname(malloc(mylen), mylen); . free(host);
char host[mylen];
.
hostname(host, mylen);
char const*const host = hostname();
The later is then equivalent to
char tmp[HOSTNAME_MAX] = { 0 }; char const*const host = hostname(tmp, HOSTNAME_MAX);
but without leaving a non-const access to the contents of tmp.
It uses a temporary value that is only valid inside the block in which the get_hostname macro is expanded. The handling of this temporary is implicit; neither the caller nor the callee have to worry of allocating or deallocating it. On the calling side this convention is simple to use without having the callee expose a static buffer.
In P99, it is currently applied in a few places, in particular in the header file "p99_posix_default.h". Its use will probably grow in future releases.
1.7.6.1