/* PIP - Platform Independent Primitives Plugin helpers Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #ifndef PIP_FREERTOS #include "piplugin.h" #include "pifile.h" #include "piincludes_p.h" /*! \class PIPluginLoader * \brief Plugin loader * * \section PIPluginLoader_sec0 Synopsis * This class provides several macro to define plugin and %PIPluginLoader - class * to load and check plugin. * * \section PIPluginLoader_sec1 Plugin side * Plugin is a shared library that can be loaded in run-time. * This is only PIP_PLUGIN macro necessary to define plugin. * If you want to set and check some version, use macro * \a PIP_PLUGIN_SET_USER_VERSION(version). Also you can * define a function to merge static sections between application * and plugin with macro \a PIP_PLUGIN_STATIC_SECTION_MERGE. Before * merge, you should set pointers to that sections with macro * \a PIP_PLUGIN_ADD_STATIC_SECTION(type, ptr). * * \section PIPluginLoader_sec2 Application side * Application should use class \a PIPluginLoader to load * plugin. Main function is \a load(PIString name). * "name" is base name of library, %PIPluginLoader * try to use sevaral names, , lib and * "dll", "so" and "dylib" extensions, depends on system. * For example: * \code * PIPluginLoader l; * l.load("foo"); * \endcode * On Windows, try to open "foo", "libfoo", "foo.dll" and * "libfoo.dll". * If you using user version check, you should set it * with macro \a PIP_PLUGIN_SET_USER_VERSION(version). * When plugin is successfully loaded and checked, * you can load your custom cymbols with function * \a resolve(name), similar to PILibrary. * \note You should use PIP_PLUGIN_EXPORT and "export "C"" * with functions you want to use with \a resolve(name)! * * \section PIPluginLoader_sec3 Static sections * Macro \a PIP_PLUGIN_STATIC_SECTION_MERGE defines function * with arguments (int type, void * from, void * to), so you * can leave this macro as declaration or define its body next: * \code * PIP_PLUGIN_STATIC_SECTION_MERGE() { * switch (type) { * ... * } * } * \endcode * \note If you using singletones, remember that cpp-defined * singletones in shared libraries are single for whole application, * including plugins! But if you use h-defined singletones or * static linking, there are many objects in application and you * should merge their content with this macro. * * Anyway, if this is macro \a PIP_PLUGIN_STATIC_SECTION_MERGE, it * called once while loading plugin with "from" - plugin side * and "to" - application side, and second (optionally) on method * \a mergeStatic() with "from" - application side and "to" - plugin side. * First direction allow you to copy all defined static content from plugin * to application, and second - after loading all plugins (for example) * to copy static content from application (and all plugins) to plugin. * * \section PIPluginLoader_sec4 Examples * Simple plugin: * \code * #include * * PIP_PLUGIN * * extern "C" { * PIP_PLUGIN_EXPORT void myFunc() { * piCout << "Hello plugin!"; * } * } * \endcode * * Application: * \code * #include * int main() { * PIPluginLoader pl; * pl.load("your_lib"); * if (pl.isLoaded()) { * typedef void(*MyFunc)(); * MyFunc f = (MyFunc)pl.resolve("myFunc"); * if (f) f(); * } * return 0; * } * \endcode * * Complex plugin: * \code * #include * * PIStringList global_list; * * PIP_PLUGIN * PIP_PLUGIN_SET_USER_VERSION("1.0.0") * PIP_PLUGIN_ADD_STATIC_SECTION(1, &global_list) * * STATIC_INITIALIZER_BEGIN * global_list << "plugin_init"; * STATIC_INITIALIZER_END * * PIP_PLUGIN_STATIC_SECTION_MERGE { * PIStringList * sfrom = (PIStringList*)from, * sto = (PIStringList*)to; * *sto << *sfrom; * sto->removeDuplicates(); * } * \endcode * * Application: * \code * #include * * PIStringList global_list; * * PIP_PLUGIN_SET_USER_VERSION("1.0.0"); * PIP_PLUGIN_ADD_STATIC_SECTION(1, &global_list); * * int main() { * global_list << "app"; * PIPluginLoader pl; * pl.load("your_lib"); * pl.mergeStatic(); * piCout << "list =" << global_list; * return 0; * } * \endcode * */ #define STR_WF(s) #s #define STR(s) STR_WF(s) PIPluginInfo::PIPluginInfo() { in_plugin = false; } void PIPluginInfo::setUserVersion(const PIString & v) { user_version[in_plugin ? 1 : 0] = v; } void PIPluginInfo::setStaticSection(int type, void * ptr) { static_sections[in_plugin ? 1 : 0][type] = ptr; } PIString PIPluginInfo::userVersion(bool plugin) const { return user_version[plugin ? 1 : 0]; } PIMap PIPluginInfo::staticSections(bool plugin) const { return static_sections[plugin ? 1 : 0]; } void PIPluginInfo::enterPlugin() { in_plugin = true; } PIPluginInfo * PIPluginInfo::instance() { static PIPluginInfo ret; return &ret; } PIPluginLoader::PIPluginLoader(const PIString & name) { func_loader_version = nullptr; func_plugin_info = nullptr; func_static_merge = nullptr; plugin_info = nullptr; loaded = false; if (!name.isEmpty()) load(name); } PIPluginLoader::~PIPluginLoader() { lib.unload(); } bool PIPluginLoader::load(const PIString & name) { unload(); if (!lib.load(findLibrary(name))) return false; //piCout << "loading" << lib.path() << "..."; func_loader_version = (FunctionLoaderVersion)lib.resolve(STR(__PIP_PLUGIN_LOADER_VERSION_FUNC__)); if (!func_loader_version) { piCout << "Load plugin \"" << name << "\" error: can`t find" << STR(__PIP_PLUGIN_LOADER_VERSION_FUNC__); unload(); return false; } if (__PIP_PLUGIN_LOADER_VERSION__ != func_loader_version()) { piCout << "Load plugin \"" << name << "\" error: invalid loader version: plugin" << __PIP_PLUGIN_LOADER_VERSION__ << "!=" << func_loader_version(); unload(); return false; } func_plugin_info = (FunctionPluginInfo)lib.resolve(STR(__PIP_PLUGIN_PLUGIN_INFO_FUNC__)); if (!func_plugin_info) { piCout << "Load plugin \"" << name << "\" error: can`t find" << STR(__PIP_PLUGIN_PLUGIN_INFO_FUNC__); unload(); return false; } plugin_info = func_plugin_info(); if (!plugin_info) { piCout << "Load plugin \"" << name << "\" error: null PIPluginInfo"; unload(); return false; } if (PIPluginInfo::instance()->userVersion(false).size_s() > 1) { PIString pversion = plugin_info->userVersion(true), lversion = PIPluginInfo::instance()->userVersion(false); if (pversion != lversion) { piCout << "Load plugin \"" << name << "\" error: invalid user version: plugin" << pversion << "!=" << lversion; unload(); return false; } } func_static_merge = (FunctionStaticMerge)lib.resolve(STR(__PIP_PLUGIN_STATIC_MERGE_FUNC__)); if (func_static_merge) { auto pss = plugin_info->staticSections(true), lss = PIPluginInfo::instance()->staticSections(false); piCout << lss.keys() << pss.keys(); auto it = lss.makeIterator(); while (it.next()) { if (!pss.contains(it.key())) continue; void * from = pss.value(it.key()), * to = it.value(); if (from != to) func_static_merge(it.key(), from, to); } } loaded = true; return true; } void PIPluginLoader::unload() { lib.unload(); plugin_info = nullptr; loaded = false; } bool PIPluginLoader::isLoaded() const { return loaded; } PIString PIPluginLoader::libPath() { return lib.path(); } void * PIPluginLoader::resolve(const char * name) { if (!loaded) return nullptr; return lib.resolve(name); } void PIPluginLoader::mergeStatic() { if (!loaded || !func_static_merge || !plugin_info) return; auto pss = plugin_info->staticSections(true), lss = PIPluginInfo::instance()->staticSections(false); auto it = lss.makeIterator(); while (it.next()) { if (!pss.contains(it.key())) continue; void * from = it.value(), * to = pss.value(it.key()); if (from != to) func_static_merge(it.key(), from, to); } } PIString PIPluginLoader::findLibrary(const PIString & path) { static const PIStringList prefixes({"", "lib"}); static const PIStringList suffixes({"", libExtension()}); PIFile::FileInfo fi(path); PIString dir = fi.dir(), name = fi.name(); piForeachC (PIString & p, prefixes) { piForeachC (PIString & s, suffixes) { PIString fn = dir + p + name + s; if (PIFile::isExists(fn)) return fn; } } return PIString(); } PIString PIPluginLoader::libExtension() { return #ifdef WINDOWS ".dll" #elif defined(MAC_OS) ".dylib" #else ".so" #endif ; } #endif // PIP_FREERTOS