Hot Reloading C++ Code (or How I Learned to Stop Worrying and Loathe the Linker)
CAVEAT: I am by no means an expert on these topics. Your mileage may vary.
Recently I’ve been programming a stealth game that I’m going to use to test two different AI techniques. To try and achieve this in a timely manner I did a bit of digging into hot-reloading C++ code. I’ve compiled what I discovered and developed into this blog post.
What Is “Hot Reloading”?
Hot reloading is only reloading parts of an application that have changed without reloading non-changed parts. This can be very useful in some environments, particularly game development.
Why use it?
For very large projects compile times can become a serious bottleneck for testing; minimising the amount of recompiling that must be done becomes a necessity to ensure development speed is maintained. In C++ this is a particularly nasty problem, especially when you start adding templates and header files dependencies (which in turn lead to further dependencies etc.). There are various different ways of attempting to negate this as much as possible. Hot reloading is one of them.
I will admit my use case doesn’t really meet the criteria above. Mine is for purely educational purposes because I’ve been curious as to how you would feasibly implement hot reloading.
C++ and Hot Reloading
In general hot reloading isn’t a new idea. Currently the best example I can give in C++ is Unreal Engine 4. The general idea C++ game code can be changed and UE4 will detect this and reload it.
To be able to discuss the actual implementation I need to explain some background on how dynamic linking in C++ works. There are two ways of linking to a dynamic library in C++. For the sake of simplicity (and because my implementation is Windows-only at the moment) we’ll use Microsoft’s terminology from this article.
Implicit Dynamic Linking
Implicit linking is the default type of dynamic linking. For an executable program the operating system attempts to load the dynamic library based on information stored by the program. On Windows this is done by linking “import libraries” (libraries that contain a description of the what the program is looking for) to the executable when it is built, however I believe this is not the case in other environments. This is the default option for dynamic linking nowadays.
Explicit Dynamic Linking
Explicit linking is done at run-time by the application. This means it manually manages the loading and unloading of the library in memory. This includes getting pointers to the library’s functions. On Windows (using MSVC) this is achieved using the functions LoadLibrary(), GetProcAddress() and FreeLibrary().
Hot Reloading using Explicit Dynamic Linking
As you may have figured out by now, explicit linking allows you to load and unload the dynamic library’s code from memory. This makes it the ideal fit for hot reloading code because provided the the whole application doesn’t need to be recompiled if just the library code has been changed, and as a result this is my current method of hot reloading.
Implementing the aforementioned method can be done in a variety of ways, so I want to show and explain the current version of my implementation through some code.
The AIModule class is the basis for all external AI libraries (henceforth referred to as AIModules). All AIModules share this common interface to allow for easier extension and modification of individual modules while allowing for maximum code reuse in the application itself. The library must also contain a factory function to return a new instance of the library’s module. Some code from an example AIModule is shown below.
//if it is being exported to the DLL
//declare this definition to Microsoft's extension for exporting to DLL
#define EXAMPLE_AI_MODULE __declspec(dllexport)
//otherwise declare this definition to Microsoft's extension for importing from a DLL
#define EXAMPLE_AI_MODULE __declspec(dllimport)
class EXAMPLE_AI_MODULE ExampleAIModule : public AIModule
//(put declarations for class members here)
//extern "C" prevents name mangling which in turn makes GetProcAddress calls simpler
EXAMPLE_AI_MODULE AIModule* LoadAIModule()
return new ExampleAIModule();
The AIModuleManager class is used for loading, and unloading the AIModule library handles. On Windows this means storing HINSTANCE variables from when the library is loaded until it is freed from memory. Some code from the header file is shown below.
//included for dynamic storage container
//required for the HINSTANCE definition
//typedef for convenience when using GetProcAddress
//this is the type used to store a function pointer to a module's
typedef AIModule* (*ModuleInitialiser)();
//member functions to get the module handle, load the module
//and unload the module
AIModule* const getModule(const std::string& module_name);
AIModule* const load(const std::string& module_name);
bool unload(const std::string& module_name);
//vector of pairs for contiguous storage
std::vector<std::pair<std::string, HINSTANCE>> modules;
The current way I handle adding, removing and getting module handles is done by iterating through the vector and adding/removing/getting the function for loading the module and calling it.
Implementing a very basic version of hot reloading has been very interesting. My understanding of dynamic linking has been improved significantly, especially for explicit dynamic linking. I might post about this again in the not too distant future, as I’m pretty sure I’ll have some more interesting insights to share.