There's a way to actually do this, but it requires the compiler to support #include_next
. GCC has this, no idea about other compilers. It also needs to support at least C++11.
I wouldn't exactly call this trick beautiful, but it does the job.
Ensure your include path has the the directory where the "extension" file resides before the directory where the original code resides (i.e. if the original Abc.hpp
is in src
, then move it to src/some_dir
). So in this case your include dirs would be -Isrc -Isrc/some_dir
.
Your "extension" code should be in a file with the exact same name as the original code. So for this example that's Abc.hpp
.
Here's the extension file's content:
#ifndef ABC_EXT_HPP_
#define ABC_EXT_HPP_
#include <utility>
namespace evil {
// Search the include path for the original file.
#include_next "Abc.hpp"
}
class Abc : public evil::Abc {
public:
/*
// Inherit all constructors from base class. Requires GCC >=4.8.
using evil::Abc::Abc;
*/
/* Use the solution below if your compiler supports C++11, but not
* inheriting constructors.
*/
template <class... Args>
Abc (Args... args) : evil::ABC(std::forward<Args...>(args...)) { }
~Abc () { }
void methodAdded () { /* Do some magic. */ }
};
#endif // ABC_EXT_HPP_
There's things missing in the example such as the assignment operator not being "forwarded" to the base class. You can use the same trick as used for the constructor to do that. There might be other things missing, but this should give you a starting point which works well enough for "simple" classes.
One thing I dislike is the creation of the "evil" namespace. However, anonymous namespaces can't help out here, because a new anonymous namespace will be created in each translation unit that includes Abc.hpp
. That will lead to issues if your base class has e.g. static members.
Edit: Nevermind, the assignment operator (i.e. Abc bla = evil::Abc(9)
) also works, because evil:Abc
can be implicitly converted to Abc
because that constructor exists.
Edit 2: You might run into a lot of trouble once there's nested namespaces involved. This happens as soon as there's an #include
in the original Abc.hpp
, because it will now be nested inside the evil
namespace. If you know all of the includes, you could include them before declaring the evil
namespace. Things get real ugly, real quick though.