14#define PLUGINS_EXTENSION ".dll"
15#define PLUGINS_PREFIX ""
19#define PLUGINS_EXTENSION ".dylib"
21#define PLUGINS_EXTENSION ".so"
23#define PLUGINS_PREFIX "lib"
57 other.handle =
nullptr;
76 explicit operator bool()
const {
return handle !=
nullptr; }
104 template <std::derived_from<IPlugin> T>
105 std::shared_ptr<T>
loadPlugin(
const std::string &path,
const std::string_view &pluginPrefix =
"")
107 std::scoped_lock lock(
m_mutex);
121 std::unique_ptr<IPlugin> plugin(entry());
124 throw std::runtime_error(
"EntryPoint returned null");
127 T *typed =
dynamic_cast<T *
>(plugin.get());
130 throw std::runtime_error(
"Plugin type mismatch");
133 auto [it, inserted] =
m_plugins.emplace(path, std::move(plugin));
136 throw std::runtime_error(
"Failed to store plugin");
141 std::shared_ptr<IPlugin> baseShared(it->second.get(), [](
IPlugin *) {});
142 return std::shared_ptr<T>(baseShared, typed);
144 catch (
const std::exception &e)
153 std::unordered_map<std::string, std::unique_ptr<IPlugin>>
m_plugins;
164 LoadLibraryA(path.c_str());
166 dlopen(path.c_str(), RTLD_LAZY);
168 if (handle ==
nullptr)
170 const std::string dlErrorMsg =
172 "LoadLibraryA failed";
176 throw std::runtime_error(
"Cannot load library: " + dlErrorMsg +
"\nWith path: " + path);
194 if (entry ==
nullptr)
196 throw std::runtime_error(
"EntryPoint not found in plugin: " + path);
206 static void validatePluginPath(
const std::filesystem::path &path,
const std::string_view &pluginPrefix)
208 const std::string filename = path.filename().string();
210 if (path.extension() != PLUGINS_EXTENSION)
212 throw std::runtime_error(
"Invalid plugin extension: " + filename +
213 " (expected " PLUGINS_EXTENSION
")");
216 if (!filename.starts_with(pluginPrefix))
218 throw std::runtime_error(
"Invalid plugin name: " + filename +
" (plugins must start with '" +
219 std::string(pluginPrefix) +
"')");
Modern, type-safe plugin loader.
std::unordered_map< std::string, std::unique_ptr< IPlugin > > m_plugins
PluginLoader(PluginLoader &&)=delete
static EntryPointFn getEntryPoint(SharedLib &lib, const std::string &path)
PluginLoader & operator=(PluginLoader &&)=delete
std::shared_ptr< T > loadPlugin(const std::string &path, const std::string_view &pluginPrefix="")
Load a plugin of type T.
PluginLoader & operator=(const PluginLoader &)=delete
PluginLoader(const PluginLoader &)=delete
static void validatePluginPath(const std::filesystem::path &path, const std::string_view &pluginPrefix)
Validate the plugin path.
std::unordered_map< std::string, SharedLib > m_handles
static SharedLib loadLibrary(const std::string &path)
Load a shared library.
IPlugin *(*)() EntryPointFn
RAII wrapper for shared libraries.
SharedLib & operator=(const SharedLib &)=delete
SharedLib(const LibHandle h=nullptr)
SharedLib & operator=(SharedLib &&other) noexcept
SharedLib(const SharedLib &)=delete
SharedLib(SharedLib &&other) noexcept