Compilation of this example:
gcc -c -D NAMESPACE=First_ first.c -o First_first.o
gcc -c -D NAMESPACE=Second_ second.c -o Second_second.o
gcc -c -D NAMESPACE=Third_ third.c -o Third_third.o
gcc -c -D NAMESPACE=Fourth_ fourth.c -o Fourth_fourth.o
gcc -c -D NAMESPACE=FirstAgain_ first.c -o FirstAgain_first.o
gcc -c -D NAMESPACE=SecondAgain_ second.c -o SecondAgain_second.o
gcc -c main.c -o main.o
gcc main.o First_first.o Second_second.o Third_third.o Fourth_fourth.o FirstAgain_first.o SecondAgain_second.o -o main.exeThere are limitations with this namespacing macro pattern!
Firstly we rely on #pragma once for our headers. This is because inclusion
guard macros cannot be namespaced. Macro identifiers are always literal, it is
impossible to define a macro identifier from an expression. Thus using inclusion
guards would result in namespace pollution, which is what we're trying to avoid
with this namespacing pattern. If we allowed inclusion guards, that would defeat
the point of using this namespacing pattern! Note that #pragma once relies on
file characteristics and file path, whereas inclusion guards rely on a unique
macro constant.
Ideally we would have preferred using something like:
#ifndef NS(FILE_H)
#define eval(NS(FILE_H))
// ...
#endifWe need something to disambiguate #define NS(FILE_H), does it mean to define
NS() macro expression again, or does it mean to evaluate NS(FILE_H) and use
the resulting value as the new macro identifier?
The #pragma once has wide support: https://en.wikipedia.org/wiki/Pragma_once#Portability
Beware there are big caveats with #pragma once:
With or without #pragma once in our header files, header files that include
included header files can result in namespace conflicts. The namespace conflicts
occur in opposite ways.
With #pragma once, when a header file that includes an included header file,
and sets a different namespace from the prior inclusion for the current
inclusion, a compilation failure can occur because the current inclusion will
be not be included. The solution is to make all inclusions of a particular
header file use the same namespace.
Without #pragma once, when a header file that includes an included header file,
and sets the same namespace for the current inclusion that was used in the prior
inclusion, a compilation failure can occur due to multiple definitions caused
by multiple inclusion. The solution is to make all inclusions of a particular
header file use different namespaces.
Out of the 2 above situations, which is the more desirable? I'd argue the former should be preferred from the latter. The latter forces you compile a separate namespaced object file for linking, for every repeated inclusion. This is pretty inefficient! The former situation allows empty namespaced libraries to be included normally without any problems. Only once you meet a namespace clash with a empty namespace, do you consider adding a custom namespace. At this point you just need to remember that this custom namespace should be applied to every inclusion of the same library.
So just remember, don't namespace (just means leave the namespace empty) until you need it. It will reduce headaches under repeated nested inclusion.
Note that the above mentioned problem does not apply to separate compilation of object files. Each object file can be compiled from the same libraries with different or the same namespace without any problems. They are later just linked together. However for efficiency reasons, it may be better for all the linked objects to standardise on the namespace.
Secondly we rely on #pragma push_macro and #pragma pop_macro for libraries
that need their own namespace while namespacing their own dependencies. This is
required because the preprocessor doesn't perform imperatively. See:
- http://stackoverflow.com/questions/14261534/temporarily-overwrite-a-macro-in-c-preprocessor
- http://stackoverflow.com/questions/1543736/how-do-i-temporarily-disable-a-macro-expansion-in-c-c
Ideally we would have preferred using "temporary macro variables":
#define THIS_NAMESPACE NAMESPACE
// ...
#define NAMESPACE THIS_NAMESPACEWhich should be the equivalent of:
var x = y;
y = x;
But it doesn't work because the C preprocessor is globally declarative, not imperative.
The #pragma push_macro and #pragma pop_macro only works in MSVC, GCC and
Clang.