Sane C++ Libraries
C++ Platform Abstraction Libraries
Loading...
Searching...
No Matches
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 "Libraries/Async/Async.h"
#include "Libraries/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(PluginCompiler::findBestCompiler(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()
{
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::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 unsigned int PluginHash(const char(&str)[N])
Compute compile time FNV hash for a char array.
Definition PluginHash.h:33
StringView relativePath
Relative path of the file being notified from basePath
Definition FileSystemWatcher.h:107
A structure to describe modified time.
Definition FileSystem.h:304
Time::Realtime modifiedTime
Time when file was last modified.
Definition FileSystem.h:306
Result setLastModifiedTime(StringView file, Time::Realtime time)
Change last modified time of a given file.
Result getFileStat(StringView file, FileStat &fileStat)
Obtains stats (size, modified time) about a file.
Compiles a plugin to a dynamic library.
Definition Plugin.h:113
SmallVector< StringNative< 256 >, 8 > includePaths
Path to include directories used to compile plugin.
Definition Plugin.h:144
Type type
Compile Type.
Definition Plugin.h:140
Plugin description, category, dependencies, files and directory location.
Definition Plugin.h:54
SmallString< 255 > description
Long description of plugin.
Definition Plugin.h:56
SmallVector< PluginIdentifier, 8 > dependencies
Dependencies necessary to load this plugin.
Definition Plugin.h:60
PluginIdentity identity
Uniquely identifier a plugin.
Definition Plugin.h:55
SmallVector< SmallString< 10 >, 8 > build
Build options.
Definition Plugin.h:61
SmallString< 10 > category
Category where plugin belongs to.
Definition Plugin.h:57
A plugin dynamic library loaded from a SC::PluginRegistry.
Definition Plugin.h:188
SystemDynamicLibrary dynamicLibrary
System handle of plugin's dynamic library.
Definition Plugin.h:190
bool queryInterface(T *&outInterface) const
Try to obtain a given interface as exported by a plugin through SC_PLUGIN_EXPORT_INTERFACES macro.
Definition Plugin.h:199
SmallString< 10 > version
Plugin version (x.y.z)
Definition Plugin.h:44
SmallString< 30 > name
Plugin name.
Definition Plugin.h:43
Holds a registry of plugins, loading and compiling them on the fly.
Definition Plugin.h:224
Result unloadPlugin(const StringView identifier)
Unloads an already loaded plugin by its identifier.
Result loadPlugin(const StringView identifier, const PluginCompiler &compiler, const PluginSysroot &sysroot, StringView executablePath, LoadMode loadMode=LoadMode::Load)
Loads a plugin with given identifier, compiling it with given PluginCompiler.
Result removeAllBuildProducts(const StringView identifier)
Removes all temporary build products of the Plugin with given identifier.
const PluginDynamicLibrary * findPlugin(const StringView identifier)
Find a PluginDynamicLibrary in the registry with a given identifier.
Result replaceDefinitions(Vector< PluginDefinition > &&definitions)
Appends the definitions to registry.
void getPluginsToReloadBecauseOf(StringView relativePath, Time::Milliseconds tolerance, Function< void(const PluginIdentifier &)> onPlugin)
Enumerates all plugins that must be reloaded when relativePath is modified.
Holds include and library paths for a system toolchain, used to let plugins link to libc and libc++.
Definition Plugin.h:163
StringView libraryRootDirectory
Path to sources directory for library.
Definition Testing.h:27
StringView executableFile
Path to current executable.
Definition Testing.h:28
Type-safe wrapper of uint64 used to represent milliseconds.
Definition Time.h:44