I've gotten into a bit of a design block in a C++ program of mine as two different header files are required to reference each other. Typically a forward declaration would be used here, but since both classes use template functions/constructors a forward declaration cannot be used as methods/variables from both classes need to be used.
For example consider the following scenario (this is pseudo code as an example, it may/may not compile. The objects are representative of my actual application so if a redesign is necessary then I'd love to understand the design philosophies of what I did wrong)
// Application.hpp
#include <Assets.hpp>
#include <Logger.hpp>
class Application {
public:
// Some brilliant code here ...
Logger myLogger;
template <int someArrayLen> Application(std::array<int, someArrayLen> myArr, SomeOtherTypes someOtherStuff) : myLogger(stuffHere) {
mainAssets = new Assets(myArr);
}
~Application(); // Assume this is implemented in Application.cpp and has delete mainAssets;
};
extern Application* mainApp; // Assume Application* mainApp = nullptr; in Application.cpp
// Assets.hpp
// #include <Application.hpp> ???? The issue lies here
class Assets {
private:
// Random data structures/stuff for holding shaders/textures/etc
protected:
template <int someArrayLen> Assets(std::array<int, someArrayLen> myArr) {
if (!shadersSupported()) {
// Main app is an unknown symbol
mainApp->myLogger->error("Your GPU is too old/whatever!");
}
// Random code for loading assets based on my template stuff
}
friend class Application;
public:
// Get assets/whatever here
};
extern Assets* mainAssets; // Assume Assets* mainAssets = nullptr; in Assets.cpp
How can I fix the compile error regarding mainApp
being an unknown symbol? Any feedback/help is appreciated, thanks!
I've already looked through all the following questions but none address this unique scenario:
I've also already considered the following:
Forward declarations will indeed work for your problem. The key is that function templates can be defined out of line (i.e., not in your class ... { };
declaration) legally. The same can be achieved for arbitrary functions using the inline
keyword.
To now solve your specific problem, just split Application.hpp
into Applicaton_fwd.hpp
and Application.hpp
- similar to iosfwd
. Application_fwd.hpp
contains almost all the code and Application.hpp
includes Application_fwd.hpp
and Assets.hpp
before defining the Application::Application
function template (just like you would define a function in a *.cpp
file).
In Assets.hpp
, you can simply use Application_fwd.hpp
as long as you do not use the constructor. If you also use the Application
constructor in Assets.hpp
, things become a bit more complicated in that you need to very carefully consider all possible inclusion scenarios (i.e., what happens exactly every time one of your headers is included by themselves or a user) to make sure that it resolves in the order that you need it to without the guards causing trouble.
You can see it in action here