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.
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:
Example #1
This GCC distribution at least have a STL library that indirectly include
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:
Later you decide to refactor the code and use internally another class:
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.
Directory structure:
MyClass.h: including this file actually cause the inclusion of "Foo.h".
MyClass.cpp
Foo.h
Directory structure:
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
mylib/Foo.h
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
Note how important is the usage of "relative path" inclusion
mylib/ priv/ MyClass.cpp
mylib/ priv/ Foo.h
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).
Without forwarding:
With forwarding:
Pros
14/11/2015 17:10 added usage example
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
- Increased compile time
- More maintenance cost for library developers
Article updates
14/11/2015 17:10 added usage example