Система плагинов позволяет расширять функциональность приложения без модификации его исходного кода. Вот профессиональный подход к реализации такой системы на C++.
Для системы плагинов потребуются:
// Базовый интерфейс для всех плагинов
class IPlugin {
public:
virtual ```IPlugin() = default;
// Методы, которые должны реализовать все плагины
virtual std::string getName() const = 0;
virtual std::string getVersion() const = 0;
virtual void initialize() = 0;
virtual void execute() = 0;
virtual void shutdown() = 0;
};
// Макрос для экспорта символов (зависит от компилятора)
#ifdef _WIN32
#define PLUGIN_EXPORT __declspec(dllexport)
#else
#define PLUGIN_EXPORT __attribute__((visibility("default")))
#endif
// Функция, которую должен экспортировать каждый плагин
extern "C" PLUGIN_EXPORT IPlugin* createPlugin();
Пример простого плагина:
#include "IPlugin.h"
class HelloPlugin : public IPlugin {
public:
std::string getName() const override { return "HelloPlugin"; }
std::string getVersion() const override { return "1.0.0"; }
void initialize() override {
std::cout << "HelloPlugin initialized\n";
}
void execute() override {
std::cout << "Hello from plugin!\n";
}
void shutdown() override {
std::cout << "HelloPlugin shutdown\n";
}
};
extern "C" PLUGIN_EXPORT IPlugin* createPlugin() {
return new HelloPlugin();
}
Реализация загрузки плагинов с использованием динамических библиотек:
#include <dlfcn.h> // Unix
// или #include <windows.h> для Windows
class PluginLoader {
public:
IPlugin* loadPlugin(const std::string& path) {
// Загрузка библиотеки
void* handle = dlopen(path.c_str(), RTLD_LAZY);
if (!handle) {
throw std::runtime_error("Cannot load plugin: " + std::string(dlerror()));
}
// Получение функции создания плагина
auto createFunc = reinterpret_cast<IPlugin*(*)()>(dlsym(handle, "createPlugin"));
if (!createFunc) {
dlclose(handle);
throw std::runtime_error("Invalid plugin interface");
}
// Создание экземпляра плагина
IPlugin* plugin = createFunc();
plugin->initialize();
// Сохраняем handle для последующего закрытия
pluginHandles_[plugin] = handle;
return plugin;
}
void unloadPlugin(IPlugin* plugin) {
if (pluginHandles_.count(plugin)) {
plugin->shutdown();
dlclose(pluginHandles_[plugin]);
delete plugin;
pluginHandles_.erase(plugin);
}
}
private:
std::unordered_map<IPlugin*, void*> pluginHandles_;
};
Для управления множеством плагинов полезно создать реестр:
class PluginRegistry {
public:
void registerPlugin(IPlugin* plugin) {
plugins_[plugin->getName()] = plugin;
}
void unregisterPlugin(const std::string& name) {
if (plugins_.count(name)) {
loader_.unloadPlugin(plugins_[name]);
plugins_.erase(name);
}
}
IPlugin* getPlugin(const std::string& name) {
return plugins_.count(name) ? plugins_[name] : nullptr;
}
void loadAll(const std::string& pluginsDir) {
for (const auto& entry : std::filesystem::directory_iterator(pluginsDir)) {
if (entry.path().extension() == ".so" ||
entry.path().extension() == ".dll") {
try {
IPlugin* plugin = loader_.loadPlugin(entry.path());
registerPlugin(plugin);
} catch (const std::exception& e) {
std::cerr << "Failed to load plugin: " << e.what() << "\n";
}
}
}
}
private:
PluginLoader loader_;
std::unordered_map<std::string, IPlugin*> plugins_;
};
Для более сложных систем можно добавить:
Именование файлов:
.dll
.so
(Linux), .dylib
(macOS)Загрузка библиотек:
LoadLibrary
, GetProcAddress
dlopen
, dlsym
Экспорт символов:
__declspec(dllexport)
__attribute__((visibility("default")))
Ключевые моменты при разработке системы плагинов на C++:
Такая система позволит легко расширять функциональность приложения без перекомпиляции основного кода.