Skip to main content
GameDev.net gamedev.net

PRO Tired of ads? Read GameDev.net ad-free and help keep the community independent with GameDev Pro — $3/month.

Making Your C++ Namespace Solid and Future-Proof
Article

Making Your C++ Namespace Solid and Future-Proof

Minimize namespace pollution and reduce chance of breaking changes (compilation errors) on client code when you ship the next version of your awesome library

Dario Oliveri
November 17, 2015 40.4k views

This article provides a possible solution to a real problem, nothing more, nothing less. It is up to developers evaluating pros and cons to decide if this approach is worthwhile for their framework/library. The final goal is to mimik the "import" feature of languages like Java or C#. That will be usefull for large codebases or standard codebases, but it is of no use for small code-bases or home-made projects.

The problem

In C++ you do not import stuff, you include files which means "text replacement" and some preprocessor magic. When you include a file you are indirectly including many other files, relying on this behaviour is bad and can cause harm in the long run:
  • What you expect "by design" is that you have at your disposal only what you "imported/included"
  • Side-included headers are actually only a implementation detail: it may change!

Two real examples of breaking code

Example #1 This GCC distribution at least have a STL library that indirectly include <functional> from other headers, when you accidentally use stuff from such a header then the code will just compile fine, but when you try to compile the code from elsewhere the compilers will complain that there is no such thing called "std::function" and (maybe) you are missing some include (and you are truly missing an include). Example #2 Your class is using another class as a private member: #include "Foo.h" // a implementation detail class MyClass{ Foo _foo; public: //... }; Later you decide to refactor the code and use internally another class: #include "Bar.h" //ops.. any client used "foo" but not included it? => dang compile error for him class MyClass{ Bar _bar; public: //... };

The Solution

The solution to the problem is actually very simple: Put everything in another namespace, and "import" it only if client is actually including it from the right header.

Your library BEFORE

Directory structure: mylib/ +MyClass.h +Foo.h +MyClass.cpp +Foo.cpp MyClass.h: including this file actually cause the inclusion of "Foo.h". #pragma once #include "Foo.h" // a implementation detail namespace mylib{ class MyClass{ Foo _foo; public: //... }; } MyClass.cpp #include "MyClass.h" // a implementation detail namespace mylib{ //... } Foo.h #pragma once namespace mylib{ class Foo{ //... }; }

Your library AFTER

Directory structure: mylib/ +MyClass.h +Foo.h priv/ +MyClass.h +Foo.h +MyClass.cpp +Foo.cpp You move all old files to a private folder, then you just import stuff into your namespace from public headers Forwarding headers mylib/MyClass.h #include "priv/MyClass.h" namespace PUBLIC_NAMESPACE{ using MyClass = PRIVATE_NAMESPACE::MyClass; //requires C++11 } mylib/Foo.h #include "priv/Foo.h" namespace PUBLIC_NAMESPACE{ using Foo = PRIVATE_NAMESPACE::Foo; //requires C++11 } Internally you keep everything in a private namespace, so the user is forced to include correct headers immediatly: Now entering the "priv" folder mylib/ priv/ MyClass.h #pragma once #include "Foo.h" namespace PRIVATE_NAMESPACE{ class MyClass{ Foo _foo; public: //... }; } Note how important is the usage of "relative path" inclusion mylib/ priv/ MyClass.cpp #include "MyClass.h" // a implementation detail namespace PRIVATE_NAMESPACE{ //... } mylib/ priv/ Foo.h #pragma once namespace PRIVATE_NAMESPACE{ class Foo{ //... }; } Apart from renaming namespaces, there are no major changes in the pre-existing code nor pre-processor magic, the whole task could be automated so that you get C#-style headers almost for free. Basically you can continue to develop as always because it is always possible to re-import stuff in a different namespace (even third party libraries).

Effects on client code:

Without forwarding: #include <MyClass.h> using namespace PUBLIC_NAMESPACE; int main(){ MyClass a; Foo b; //allowed (public namespace polluted) } With forwarding: #include <MyClass.h> using namespace PUBLIC_NAMESPACE; int main(){ MyClass a; Foo b; //NOT ALLOWED Compile error (need to include Foo) } Pros
  • Less pollution in public namespace
  • Users are forced to not rely on implementation details
  • Less chance to break code after library refactoring
Cons
  • Increased compile time
  • More maintenance cost for library developers

Article updates

14/11/2015 17:10 added usage example

License: gdol

Related Tutorials

Discussion

Discussion

Loading comments...

More from Dario Oliveri