SaneCppPlugin.h is a library that allows extending application compiling C++ source to executable code at runtime.
Quick Sheet
Note: The above code is simplified. For a more complete implementation of an hot-reload system see SCExample code (
HotReloadSystem.h).
Dependencies
Features
- Compile and link cpp files to dynamic library
- Unload / recompile / reload Plugin
- Reload all dependant plugins of a given one
- PluginDynamicLibrary::queryInterface allows creating contracts between Plugins or Plugins and Host
- Support creating libc++ and libc free plugins that only use Sane C++ Libraries
- Allows toolchain customization through SC::PluginSysroot and SC::PluginCompiler
- Forcefully unlock and delete dll being debugged from Visual Studio Debugger
Status
MVP
This library is expected to work correctly on macOS, Windows, and Linux using MSVC, clang and GCC compiler toolchains.
Description
The main use case is for now splitting applications in smaller pieces that can be hot-reloaded at runtime.
A secondary use case could be allowing customization on a delivered application (mainly on Desktop systems).
Plugins are always meant to be delivered in source code form (.cpp) and they're compiled on the fly.
A plugin is made of a single .cpp file and it declares itself through a special comment in the source code.
Such comment can declare the name, the version, a description / category and a list of dependencies.
A plugin can be modified, unloaded, re-compiled and re-loaded to provide additional functionality.
The list of dependencies makes it possible to find recursive dependencies and unload them before unload a plugin.
The library doesn't use a build system, but it compiles the .cpp files directly, linking it with symbols exported from
the loading executable (using bundle_loader on macOS and linking library exported from loading executable on windows).
Host executables must explicitly opt into exporting the needed Sane C++ libraries through SC::Build::Project::addExportLibraries
or SC::Build::Project::addExportAllLibraries; normal executables do not export SC symbols by default anymore.
Plugin Dynamic Libraries are compiled with nostdlib and nostdlib++ and they include a stub the allows defining some symbols needed due to not linking the C++ CRT.
Some special build flags however allow using libc, libc++ or other sysroot / compiler supplied windows.
The idea is that plugins only use functionality provided by the calling executable or by other plugins.
Exporting SC symbols without SC::Build
If the host executable is not built with SC::Build, you must manually export the Sane C++ libraries that the plugin
will use.
SC::Build::Project::addExportLibraries({"Foundation", "Memory", "Strings"}) is equivalent to adding preprocessor
defines like:
SC_EXPORT_LIBRARY_FOUNDATION=1
SC_EXPORT_LIBRARY_MEMORY=1
SC_EXPORT_LIBRARY_STRINGS=1
to the host executable build.
If exporting every library is acceptable for your host, SC::Build::Project::addExportAllLibraries() enables all
SC_EXPORT_LIBRARY_<LIBRARY>=1 switches for you.
Each public library header locally defines its export macro from one of these SC_EXPORT_LIBRARY_<LIBRARY>=1 switches,
for example:
Foundation->SC_EXPORT_LIBRARY_FOUNDATION=1Memory->SC_EXPORT_LIBRARY_MEMORY=1Strings->SC_EXPORT_LIBRARY_STRINGS=1Containers->SC_EXPORT_LIBRARY_CONTAINERS=1Async->SC_EXPORT_LIBRARY_ASYNC=1
Define only the libraries that must be visible to the plugin. These defines must be present consistently while compiling the host executable target, not while compiling the plugin source itself.
On Linux, the host executable must also export its symbols to the dynamic loader, typically with -rdynamic.
SC::Build adds this automatically when addExportLibraries is used.
On macOS and Windows no extra SC-specific define is needed beyond the SC_EXPORT_LIBRARY_<LIBRARY>=1 switches.
The plugin loader already links plugins against the host executable using the platform-specific mechanism.
On Windows, some extra care has been taken to force-unlock the .pdb file from visual studio debugger, that happens if the dll is being loaded on a program being debugged.
As of today this is all implemented using native dynamic library mechanisms that are being loaded directly in the process.
Doing the wrong thing with memory or forgetting to clean everything during shutdown can quickly crash the main executable.
Videos
This is the list of videos that have been recorded showing some usages of the library:
- Ep.22 - Hot-Reload dear imgui
- Ep.24 - Hot-Reload C++ on iOS
- Ep.25 - C++ Serialization and Reflection (with Hot-Reload)
Blog
Some relevant blog posts are:
Examples
- SCExample uses
Pluginlibrary for a simple hot-reload system - Unit test inside
PluginTest.cppshow how the API is meant to be used
Roadmap
There are plans to experiment with out of process plugins using some sort of RPC system (like Audio Unit plugins on macOS) and / or experimenting using WASM as a plugin host to eliminate such instability / security issues. Other ideas include redistribute a minimal C++ toolchain (probably a customized clang) that can compile the plugins without needing a system compiler or a sysroot, as all public headers of libraries in this project do not need any system or compiler header.
Usable Features:
- Specify directory for compiled intermediate and output files
- Parallel / Async compile and link
- Improve error handling and reporting
- Further customization of some build flags and features:
- Custom libraries to link (declared in the plugin)
- Custom include paths (declared in the plugin)
Complete Features:
- Create minimal clang toolchain to compile scripts on non-developer machines
- Integrate with Build library (once it will gain capability to build standalone without needing Xcode or Visual Studio)
- Evaluate possibility to achieve some minimal error recovery
- Easily integration with some RPC mechanism
💡 Unplanned Features:
- Compile plugins to WASM ?
- Deploy closed-source (already compiled) binary plugins
- Allow plugin to be compiled with different compiler from the one used in the Host
Statistics
| Type | Lines Of Code | Comments | Sum |
|---|---|---|---|
| Headers | 369 | 232 | 601 |
| Sources | 1528 | 273 | 1801 |
| Sum | 1897 | 505 | 2402 |