Sane C++ Libraries
C++ Platform Abstraction Libraries
All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Modules Pages
Plugin

🟨 Minimal dependency based plugin system with hot-reload (see Plugin for more details) More...

Classes

struct  SC::PluginFile
 Holds path to a given plugin source file. More...
 
struct  SC::PluginIdentity
 Represents the unique signature / identity of a Plugin. More...
 
struct  SC::PluginDefinition
 Plugin description, category, dependencies, files and directory location. More...
 
struct  SC::PluginScanner
 Scans a directory for PluginDefinition. More...
 
struct  SC::PluginCompiler
 Compiles a plugin to a dynamic library. More...
 
struct  SC::PluginSysroot
 Holds include and library paths for a system toolchain, used to let plugins link to libc and libc++. More...
 
struct  SC::PluginCompilerEnvironment
 Reads and holds CFLAGS and LDFLAGS environment variables, mainly to pass down sysroot location. More...
 
struct  SC::PluginDynamicLibrary
 A plugin dynamic library loaded from a SC::PluginRegistry. More...
 
struct  SC::PluginRegistry
 Holds a registry of plugins, loading and compiling them on the fly. More...
 

Detailed Description

🟨 Minimal dependency based plugin system with hot-reload (see Plugin for more details)

#include "../../Async/Async.h"
#include "../../FileSystemWatcher/FileSystemWatcher.h"
namespace SC
{
//-----------------------------------------------------------------------------
// IPluginContract.h
//-----------------------------------------------------------------------------
// CLIENT - HOST Contract (Interface)
struct IPluginContract
{
static constexpr auto InterfaceHash = PluginHash("IPluginContract");
Function<void(void)> onDraw;
};
//-----------------------------------------------------------------------------
// PluginClient.cpp
//-----------------------------------------------------------------------------
// CLIENT Plugin (binds to contract functions)
struct PluginClient : public IPluginContract
{
PluginClient()
{
IPluginContract::onDraw = []()
{
// Draw stuff...
};
}
// Called when plugin is init
bool init() { return true; }
// Called when plugin is closed
bool close() { return true; }
};
SC_PLUGIN_DEFINE(PluginClient);
SC_PLUGIN_EXPORT_INTERFACES(PluginClient, IPluginContract);
//-----------------------------------------------------------------------------
// PluginHost.cpp
//-----------------------------------------------------------------------------
// Plugin HOST (loads plugin, obtains interface and calls functions)
struct PluginHost
{
// Fill directories before calling create
String executablePath; // Where executable lives
String libraryRootDirectory; // Where Sane C++ Libraries live
String someLibraryDirectory; // Where 3rd party-lib headers live
String pluginsPath; // Where Plugins live
PluginRegistry registry;
Result create(AsyncEventLoop& loop)
{
eventLoop = &loop;
// Setup Compiler
SC_TRY(PluginSysroot::findBestSysroot(compiler.type, sysroot));
// Add includes used by plugins...
SC_TRY(compiler.includePaths.push_back(libraryRootDirectory.view()));
SC_TRY(compiler.includePaths.push_back(someLibraryDirectory.view()));
// Setup File System Watcher
SC_TRY(fileSystemWatcher.init(fileSystemWatcherRunner, *eventLoop));
watcher.notifyCallback.bind<PluginHost, &PluginHost::onFileChanged>(*this);
SC_TRY(fileSystemWatcher.watch(watcher, pluginsPath.view()));
return Result(true);
}
Result close()
{
SC_TRY(fileSystemWatcher.close());
eventLoop = nullptr;
return Result(true);
}
Result syncRegistry()
{
Vector<PluginDefinition> definitions;
SC_TRY(PluginScanner::scanDirectory(pluginsPath.view(), definitions))
SC_TRY(registry.replaceDefinitions(move(definitions)));
return Result(true);
}
// Call this to load a plugin with a given identifier
Result load(StringView identifier)
{
// Force reload of plugin if already loaded
SC_TRY(registry.loadPlugin(identifier, compiler, sysroot, executablePath.view(),
PluginRegistry::LoadMode::Reload));
// Obtain contract
const PluginDynamicLibrary* plugin = registry.findPlugin(identifier);
SC_TRY(plugin->queryInterface(contract));
return Result(true);
}
void draw()
{
if (contract)
{
contract->onDraw();
}
}
private:
AsyncEventLoop* eventLoop;
PluginCompiler compiler;
PluginSysroot sysroot;
IPluginContract* contract = nullptr;
FileSystemWatcher fileSystemWatcher;
FileSystemWatcher::FolderWatcher watcher;
FileSystemWatcher::EventLoopRunner fileSystemWatcherRunner;
void onFileChanged(const FileSystemWatcher::Notification& notification)
{
auto reload = [this](const PluginIdentifier& plugin) { (void)load(plugin.view()); };
registry.getPluginsToReloadBecauseOf(notification.relativePath, Time::Milliseconds(500), reload);
}
};
} // namespace SC
constexpr T && move(T &value)
Converts an lvalue to an rvalue reference.
Definition: Compiler.h:269
#define SC_TRY(expression)
Checks the value of the given expression and if failed, returns this value to caller.
Definition: Result.h:48
constexpr unsigned int PluginHash(const char(&str)[N])
Compute compile time FNV hash for a char array.
Definition: PluginHash.h:33
static Result findBestCompiler(PluginCompiler &compiler)
Look for best compiler on current system.
static Result scanDirectory(const StringView directory, Vector< PluginDefinition > &definitions)
Scans a directory for PluginDefinition.
static Result findBestSysroot(PluginCompiler::Type compiler, PluginSysroot &sysroot)
Finds a reasonable sysroot for the given compiler.