Skip to content

Instantly share code, notes, and snippets.

@CMCDragonkai
Last active August 14, 2025 01:58
Show Gist options
  • Select an option

  • Save CMCDragonkai/69e3184ef86de1e1ea1643a585148387 to your computer and use it in GitHub Desktop.

Select an option

Save CMCDragonkai/69e3184ef86de1e1ea1643a585148387 to your computer and use it in GitHub Desktop.
C: Namespaces using Macros (Namespace Macro Pattern)

Namespace Macro Pattern

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.exe

There 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))
// ...
#endif

We 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:

Ideally we would have preferred using "temporary macro variables":

#define THIS_NAMESPACE NAMESPACE
// ...
#define NAMESPACE THIS_NAMESPACE

Which 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.

#include "./first.h"
#include "./namespace.h"
int NS(addone) (int x) {
return x + 1;
}
#pragma once
#include "./namespace.h"
int NS(addone) (int);
#include "./namespace_undef.h"
#include "./fourth.h"
#pragma push_macro("NAMESPACE")
#undef NAMESPACE
#define NAMESPACE FirstAgain_
#include "./first.h"
#undef NAMESPACE
#define NAMESPACE SecondAgain_
#include "./second.h"
#undef NAMESPACE
#pragma pop_macro("NAMESPACE")
#include "./namespace.h"
int NS(addfourth) (int x) {
return FirstAgain_addone(x) + SecondAgain_addone(x);
}
#pragma once
#include "./namespace.h"
int NS(addfourth) (int);
#include "./namespace_undef.h"
#define NAMESPACE First_
#include "./first.h"
#undef NAMESPACE
#define NAMESPACE Second_
#include "./second.h"
#undef NAMESPACE
#define NAMESPACE Third_
#include "./third.h"
#undef NAMESPACE
#define NAMESPACE Fourth_
#include "./fourth.h"
#undef NAMESPACE
int main () {
return First_addone(1) + Second_addone(2) + Third_addboth(3) + Fourth_addfourth(4);
}
// Results: 2 + 3 + (4 + 4) + (5 + 5) = 23
#define CONC(A, B) CONC_(A, B)
#define CONC_(A, B) A##B
#ifndef NAMESPACE
#define NAMESPACE
#endif
#define NS(name) CONC(NAMESPACE, name)
#undef NS
#undef CONC_
#undef CONC
// No `#undef NAMESPACE` because the corresponding library needs `NAMESPACE`.
// The consumer will `#define NAMESPACE` and `#undef NAMESPACE` as necessary.
#include "./second.h"
#include "./namespace.h"
int NS(addone) (int x) {
return x + 1;
}
#pragma once
#include "./namespace.h"
int NS(addone) (int);
#include "./namespace_undef.h"
#include "./third.h"
#pragma push_macro("NAMESPACE")
#undef NAMESPACE
#define NAMESPACE FirstAgain_
#include "./first.h"
#undef NAMESPACE
#define NAMESPACE SecondAgain_
#include "./second.h"
#undef NAMESPACE
#pragma pop_macro("NAMESPACE")
#include "./namespace.h"
int NS(addboth) (int x) {
return FirstAgain_addone(x) + SecondAgain_addone(x);
}
#pragma once
#include "./namespace.h"
int NS(addboth) (int);
#include "./namespace_undef.h"
@cyisfor
Copy link

cyisfor commented Jul 24, 2021

I could never reconcile having to write a file full of #define NAMESPACE ... commands in it, and keep that synchronized with whatever the heck build system I'm using that has to specify -DNAMESPACE=.... I thought about making a namespace definition language, which those two things could be derived from in whatever the heck build system I'm using, but at that point why not simply translate the C source code itself?

So in the end, I think it best to specify a hardcoded namespace like int first_addone(int x) { return x + 1; } and then optionally putting both the .h and the .c through a text filter substituting 'first_' for 'firstagain_' in the build process, to generate the re-namespaced code. It also takes care of any macros named FIRST_WHATEVER(...) if you match case insensitively, including guard macros, and it can go into a file named 'firstagain.h' so that other modules which #include it will be able to visually specify what they mean by firstagain_addone(41)

Maybe you could even transform those other modules, also swapping #include "first.h" for #include "firstagain.h", so their code could use the first "first_" module while yours used a second one. That's kind of getting too complicated for me to really comprehend the consequences though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment