This commit is contained in:
2022-04-22 21:19:12 +03:00
parent 91216c4b17
commit 39e4d9a73c
9 changed files with 325 additions and 153 deletions

View File

@@ -26,6 +26,107 @@
#endif #endif
//! \addtogroup System
//! \{
//! \class PILibrary pilibrary.h
//!
//! \~\brief
//! \~english Run-time library
//! \~russian Run-time библиотека
//!
//! \~\details
//! \~english \section _sec0 Synopsis
//! \~russian \section _sec0 Краткий обзор
//! \~english
//! %PILibrary allow you dynamically load external library and use
//! some methods from it. %PILibrary instance contains library
//! pointer and unload library on destructor, so recommended
//! to use it with \b new creation.
//!
//! Main method of %PILibrary is \a resolve(const char *), which returns
//! "void*" pointer to requested method. One should test it
//! to \c nullptr and convert it in pointer to the required method.
//!
//! In case of C++ libraries it`s very important to use C-linkage
//! of exported methods! You may also need to mark methods for
//! export, e.g. \с __declspec(dllexport)
//!
//! \~russian
//! %PILibrary позволяет динамически загружать стороннюю библиотеку
//! и использовать оттуда методы. Экземпляр %PILibrary содержит
//! указатель на библиотеку и выгружает её в деструкторе, поэтому
//! рекомендуется создавать её с помощью \b new.
//!
//! Основной метод %PILibrary - это \a resolve(const char *), который возвращает
//! "void*" указатель на запрошенный метод. Необходимо проверить его
//! на \c nullptr и преобразовать в указатель на нужный метод.
//!
//! В случае C++ библиотеки очень важно использовать C-linkage
//! для экспортируемых методов! Также может понадобиться пометить
//! методы на экспорт, например, \с __declspec(dllexport)
//!
//! \~\code
//! extern "C" {
//! __declspec(dllexport) int exportedSum(int,int);
//! __declspec(dllexport) int exportedMul(int,int);
//! }
//! \endcode
//!
//! \~english \section _sec1 Usage
//! \~russian \section _sec1 Использование
//!
//! \~english Library:
//! \~russian Библиотека:
//! \~\code
//! #include <piplugin.h>
//!
//! extern "C" {
//! PIP_PLUGIN_EXPORT int exportedSum(int,int);
//! PIP_PLUGIN_EXPORT int exportedMul(int,int);
//! }
//!
//! int exportedSum(int a, int b) {
//! return a + b;
//! }
//! int exportedMul(int a, int b) {
//! return a * b;
//! }
//! \endcode
//!
//! \~english Program:
//! \~russian Программа:
//! \~\code
//! int main(int argc, char * argv[]) {
//! typedef int(*MyFunc)(int,int);
//! PILibrary * lib = new PILibrary();
//! if (lib->load("mylib.dll")) {
//! MyFunc fadd = (MyFunc)lib->resolve("exportedSum");
//! MyFunc fmul = (MyFunc)lib->resolve("exportedMul");
//! if (fadd) {
//! int sum = fadd(1, 2);
//! piCout << "sum =" << sum;
//! } else {
//! piCout << "Can`t resolve" << "exportedSum";
//! }
//! if (fmul) {
//! int mul = fadd(10, 20);
//! piCout << "mul =" << mul;
//! } else {
//! piCout << "Can`t resolve" << "exportedMul";
//! }
//! } else {
//! piCout << lib->lastError();
//! }
//! delete lib;
//! }
//!
//! // sum = 3
//! // mul = 30
//! \endcode
//!
//! \}
PRIVATE_DEFINITION_START(PILibrary) PRIVATE_DEFINITION_START(PILibrary)
#ifdef WINDOWS #ifdef WINDOWS
HMODULE HMODULE

View File

@@ -32,16 +32,43 @@
class PIP_EXPORT PILibrary { class PIP_EXPORT PILibrary {
public: public:
//! \~english Constructs %PILibrary and load if "path_" not empty
//! \~russian Создает %PILibrary и загружает если "path_" не пустой
PILibrary(const PIString & path_ = PIString()); PILibrary(const PIString & path_ = PIString());
//! \~english Destroy %PILibrary, unload if library was loaded
//! \~russian Уничтожает %PILibrary и выгружает библиотеку, если она была загружена
~PILibrary(); ~PILibrary();
//! \~english Load library with relative or absolute path "path_"
//! \~russian Загружает библиотеку по относительному или абсолютному пути "path_"
bool load(const PIString & path_); bool load(const PIString & path_);
//! \~english Load library with \a path() path
//! \~russian Загружает библиотеку по пути \a path()
bool load(); bool load();
//! \~english Unload library if it was loaded
//! \~russian Выгружает библиотеку, если она была загружена
void unload(); void unload();
//! \~english Obtain exported library method with name "symbol"
//! \~russian Получает экспортированный метод библиотеки с именем "symbol"
void * resolve(const char * symbol); void * resolve(const char * symbol);
//! \~english Returns if library successfully loaded
//! \~russian Возвращает успешно ли загружена библиотека
bool isLoaded() const; bool isLoaded() const;
//! \~english Returns library path
//! \~russian Возвращает путь к библиотеке
PIString path() const {return libpath;} PIString path() const {return libpath;}
//! \~english Returns last occured error in human-readable format
//! \~russian Возвращает последнюю ошибку в читаемом виде
PIString lastError() const {return liberror;} PIString lastError() const {return liberror;}
private: private:

View File

@@ -24,139 +24,144 @@
#include "pidir.h" #include "pidir.h"
#include "piincludes_p.h" #include "piincludes_p.h"
/*! \class PIPluginLoader //! \addtogroup System
* \brief Plugin loader //! \{
* //! \class PIPluginLoader piplugin.h
* \section PIPluginLoader_sec0 Synopsis //!
* This class provides several macro to define plugin and %PIPluginLoader - class //! \brief
* to load and check plugin. //! \~english Plugin loader
* //! \~russian Загрузчик плагина
* \section PIPluginLoader_sec1 Plugin side //!
* Plugin is a shared library that can be loaded in run-time. //! \section PIPluginLoader_sec0 Synopsis
* This is only PIP_PLUGIN macro necessary to define plugin. //! This class provides several macro to define plugin and %PIPluginLoader - class
* If you want to set and check some version, use macro //! to load and check plugin.
* \a PIP_PLUGIN_SET_USER_VERSION(version). Also you can //!
* define a function to merge static sections between application //! \section PIPluginLoader_sec1 Plugin side
* and plugin with macro \a PIP_PLUGIN_STATIC_SECTION_MERGE. Before //! Plugin is a shared library that can be loaded in run-time.
* merge, you should set pointers to that sections with macro //! This is only PIP_PLUGIN macro necessary to define plugin.
* \a PIP_PLUGIN_ADD_STATIC_SECTION(type, ptr). //! If you want to set and check some version, use macro
* //! \a PIP_PLUGIN_SET_USER_VERSION(version). Also you can
* \section PIPluginLoader_sec2 Application side //! define a function to merge static sections between application
* Application should use class \a PIPluginLoader to load //! and plugin with macro \a PIP_PLUGIN_STATIC_SECTION_MERGE. Before
* plugin. Main function is \a load(PIString name). //! merge, you should set pointers to that sections with macro
* "name" is base name of library, %PIPluginLoader //! \a PIP_PLUGIN_ADD_STATIC_SECTION(type, ptr).
* try to use sevaral names, \<name\>, lib\<name\> and //!
* "dll", "so" and "dylib" extensions, depends on system. //! \section PIPluginLoader_sec2 Application side
* For example: //! Application should use class \a PIPluginLoader to load
* \code //! plugin. Main function is \a load(PIString name).
* PIPluginLoader l; //! "name" is base name of library, %PIPluginLoader
* l.load("foo"); //! try to use sevaral names, \<name\>, lib\<name\> and
* \endcode //! "dll", "so" and "dylib" extensions, depends on system.
* On Windows, try to open "foo", "libfoo", "foo.dll" and //! For example:
* "libfoo.dll". //! \code
* If you using user version check, you should set it //! PIPluginLoader l;
* with macro \a PIP_PLUGIN_SET_USER_VERSION(version). //! l.load("foo");
* When plugin is successfully loaded and checked, //! \endcode
* you can load your custom symbols with function //! On Windows, try to open "foo", "libfoo", "foo.dll" and
* \a resolve(name), similar to PILibrary. //! "libfoo.dll".
* \note You should use PIP_PLUGIN_EXPORT and "export "C"" //! If you using user version check, you should set it
* with functions you want to use with \a resolve(name)! //! with macro \a PIP_PLUGIN_SET_USER_VERSION(version).
* //! When plugin is successfully loaded and checked,
* \section PIPluginLoader_sec3 Static sections //! you can load your custom symbols with function
* Macro \a PIP_PLUGIN_STATIC_SECTION_MERGE defines function //! \a resolve(name), similar to PILibrary.
* with arguments (int type, void * from, void * to), so you //! \note You should use PIP_PLUGIN_EXPORT and "export "C""
* can leave this macro as declaration or define its body next: //! with functions you want to use with \a resolve(name)!
* \code //!
* PIP_PLUGIN_STATIC_SECTION_MERGE() { //! \section PIPluginLoader_sec3 Static sections
* switch (type) { //! 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
* \endcode //! PIP_PLUGIN_STATIC_SECTION_MERGE() {
* \note If you using singletones, remember that cpp-defined //! switch (type) {
* 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. //! \endcode
* //! \note If you using singletones, remember that cpp-defined
* Anyway, if this is macro \a PIP_PLUGIN_STATIC_SECTION_MERGE, it //! singletones in shared libraries are single for whole application,
* called once while loading plugin with "from" - plugin side //! including plugins! But if you use h-defined singletones or
* and "to" - application side, and second (optionally) on method //! static linking, there are many objects in application and you
* \a mergeStatic() with "from" - application side and "to" - plugin side. //! should merge their content with this macro.
* First direction allow you to copy all defined static content from plugin //!
* to application, and second - after loading all plugins (for example) //! Anyway, if this is macro \a PIP_PLUGIN_STATIC_SECTION_MERGE, it
* to copy static content from application (and all plugins) to plugin. //! called once while loading plugin with "from" - plugin side
* //! and "to" - application side, and second (optionally) on method
* \section PIPluginLoader_sec4 Examples //! \a mergeStatic() with "from" - application side and "to" - plugin side.
* Simple plugin: //! First direction allow you to copy all defined static content from plugin
* \code //! to application, and second - after loading all plugins (for example)
* #include <piplugin.h> //! to copy static content from application (and all plugins) to plugin.
* //!
* PIP_PLUGIN //! \section PIPluginLoader_sec4 Examples
* //! Simple plugin:
* extern "C" { //! \code
* PIP_PLUGIN_EXPORT void myFunc() { //! #include <piplugin.h>
* piCout << "Hello plugin!"; //!
* } //! PIP_PLUGIN
* } //!
* \endcode //! extern "C" {
* //! PIP_PLUGIN_EXPORT void myFunc() {
* Application: //! piCout << "Hello plugin!";
* \code //! }
* #include <piplugin.h> //! }
* int main() { //! \endcode
* PIPluginLoader pl; //!
* pl.load("your_lib"); //! Application:
* if (pl.isLoaded()) { //! \code
* typedef void(*MyFunc)(); //! #include <piplugin.h>
* MyFunc f = (MyFunc)pl.resolve("myFunc"); //! int main() {
* if (f) f(); //! PIPluginLoader pl;
* } //! pl.load("your_lib");
* return 0; //! if (pl.isLoaded()) {
* } //! typedef void(*MyFunc)();
* \endcode //! MyFunc f = (MyFunc)pl.resolve("myFunc");
* //! if (f) f();
* Complex plugin: //! }
* \code //! return 0;
* #include <piplugin.h> //! }
* //! \endcode
* PIStringList global_list; //!
* //! Complex plugin:
* PIP_PLUGIN //! \code
* PIP_PLUGIN_SET_USER_VERSION("1.0.0") //! #include <piplugin.h>
* PIP_PLUGIN_ADD_STATIC_SECTION(1, &global_list) //!
* //! PIStringList global_list;
* STATIC_INITIALIZER_BEGIN //!
* global_list << "plugin_init"; //! PIP_PLUGIN
* STATIC_INITIALIZER_END //! PIP_PLUGIN_SET_USER_VERSION("1.0.0")
* //! PIP_PLUGIN_ADD_STATIC_SECTION(1, &global_list)
* PIP_PLUGIN_STATIC_SECTION_MERGE { //!
* PIStringList * sfrom = (PIStringList*)from, * sto = (PIStringList*)to; //! STATIC_INITIALIZER_BEGIN
* *sto << *sfrom; //! global_list << "plugin_init";
* sto->removeDuplicates(); //! STATIC_INITIALIZER_END
* } //!
* \endcode //! PIP_PLUGIN_STATIC_SECTION_MERGE {
* //! PIStringList * sfrom = (PIStringList*)from, * sto = (PIStringList*)to;
* Application: //! *sto << *sfrom;
* \code //! sto->removeDuplicates();
* #include <piplugin.h> //! }
* //! \endcode
* PIStringList global_list; //!
* //! Application:
* PIP_PLUGIN_SET_USER_VERSION("1.0.0"); //! \code
* PIP_PLUGIN_ADD_STATIC_SECTION(1, &global_list); //! #include <piplugin.h>
* //!
* int main() { //! PIStringList global_list;
* global_list << "app"; //!
* PIPluginLoader pl; //! PIP_PLUGIN_SET_USER_VERSION("1.0.0");
* pl.load("your_lib"); //! PIP_PLUGIN_ADD_STATIC_SECTION(1, &global_list);
* pl.mergeStatic(); //!
* piCout << "list =" << global_list; //! int main() {
* return 0; //! global_list << "app";
* } //! PIPluginLoader pl;
* \endcode //! pl.load("your_lib");
* //! pl.mergeStatic();
*/ //! piCout << "list =" << global_list;
//! return 0;
//! }
//! \endcode
//!
//! \}
#define STR_WF(s) #s #define STR_WF(s) #s
#define STR(s) STR_WF(s) #define STR(s) STR_WF(s)

View File

@@ -209,7 +209,7 @@ __THREAD_FUNC_RET__ thread_function_once(void * t) {((PIThread*)t)->__thread_fun
//! If "func" if not null this function will be executed after \a run(). //! If "func" if not null this function will be executed after \a run().
//! //!
//! ThreadFunc is any static function with format "void func(void * data)", or //! ThreadFunc is any static function with format "void func(void * data)", or
//! lambda-function with format [...]( ){...}. //! [lambda expression](https://en.cppreference.com/w/cpp/language/lambda) with format [...]( ){...}.
//! "Data" is custom data set from constructor or with \a setData() function. //! "Data" is custom data set from constructor or with \a setData() function.
//! //!
//! Also you can connect to event \a started(), but in this case you should to white //! Also you can connect to event \a started(), but in this case you should to white
@@ -221,7 +221,7 @@ __THREAD_FUNC_RET__ thread_function_once(void * t) {((PIThread*)t)->__thread_fun
//! если "ThreadFunc" существует, он будет вызываться после \a run(). //! если "ThreadFunc" существует, он будет вызываться после \a run().
//! //!
//! ThreadFunc может быть любым статическим методом в формате "void func(void * data)", либо //! ThreadFunc может быть любым статическим методом в формате "void func(void * data)", либо
//! лямбда-функцией в формате [...]( ){...}. //! [лямбда-выражение](https://ru.cppreference.com/w/cpp/language/lambda) в формате [...]( ){...}.
//! "Data" является произвольным указателем, задаваемым в конструкторе или методом \a setData(). //! "Data" является произвольным указателем, задаваемым в конструкторе или методом \a setData().
//! //!
//! Также можно присоединиться к событию \a started(), но в этом случае надо будет своими силами //! Также можно присоединиться к событию \a started(), но в этом случае надо будет своими силами
@@ -273,8 +273,8 @@ __THREAD_FUNC_RET__ thread_function_once(void * t) {((PIThread*)t)->__thread_fun
//! // thread run 1 //! // thread run 1
//! \endcode //! \endcode
//! //!
//! \~english Using with lambda-function //! \~english Using with [lambda expression](https://en.cppreference.com/w/cpp/language/lambda)
//! \~russian Использование с лямбда-функцией //! \~russian Использование с [лямбда-выражением](https://ru.cppreference.com/w/cpp/language/lambda)
//! \~\code{.cpp} //! \~\code{.cpp}
//! int main(int argc, char * argv[]) { //! int main(int argc, char * argv[]) {
//! int cnt = 0; //! int cnt = 0;
@@ -971,13 +971,13 @@ void PIThread::runOnce(PIObject * object, const char * handler, const PIString &
//! \~\details //! \~\details
//! \~english //! \~english
//! This method create %PIThread with name "name" and execute //! This method create %PIThread with name "name" and execute
//! lambda-function "func" in this thread.\n //! [lambda expression](https://en.cppreference.com/w/cpp/language/lambda) "func" in this thread.\n
//! This %PIThread automatically delete on function finish.\n //! This %PIThread automatically delete on function finish.\n
//! "func" shouldn`t have arguments. //! "func" shouldn`t have arguments.
//! //!
//! \~russian //! \~russian
//! Этот метод создает %PIThread с именем "name" и выполняет //! Этот метод создает %PIThread с именем "name" и выполняет
//! лямбда-функцию "func" в этом потоке.\n //! [лямбда-выражение](https://ru.cppreference.com/w/cpp/language/lambda) "func" в этом потоке.\n
//! %PIThread автоматически удаляется после завершения функции.\n //! %PIThread автоматически удаляется после завершения функции.\n
//! "func" не должна иметь аргументов. //! "func" не должна иметь аргументов.
//! //!

View File

@@ -173,8 +173,8 @@ public:
//! \~russian Вызывает обработчик "handler" объекта "object" в отдельном потоке //! \~russian Вызывает обработчик "handler" объекта "object" в отдельном потоке
static void runOnce(PIObject * object, const char * handler, const PIString & name = PIString()); static void runOnce(PIObject * object, const char * handler, const PIString & name = PIString());
//! \~english Call lambda-function "func" in separate thread //! \~english Call [lambda expression](https://en.cppreference.com/w/cpp/language/lambda) "func" in separate thread
//! \~russian Вызывает лямбда-функцию "func" в отдельном потоке //! \~russian Вызывает [лямбда-выражение](https://ru.cppreference.com/w/cpp/language/lambda) "func" в отдельном потоке
static void runOnce(std::function<void()> func, const PIString & name = PIString()); static void runOnce(std::function<void()> func, const PIString & name = PIString());
//! \handlers //! \handlers

View File

@@ -28,25 +28,63 @@
//! \~russian Класс для простого уведомления и ожидания в различных потоках //! \~russian Класс для простого уведомления и ожидания в различных потоках
//! //!
//! \~\details //! \~\details
//! \~english
//!
//! \~russian
//!
//!
//! \~english \section PIThreadNotifier_sec0 Synopsis //! \~english \section PIThreadNotifier_sec0 Synopsis
//! \~russian \section PIThreadNotifier_sec0 Краткий обзор //! \~russian \section PIThreadNotifier_sec0 Краткий обзор
//! \~english //! \~english
//! This class used as event mechanism between threads. One thread wait for some event,
//! and another send this event, unblocking first thread. It is useful to
//! syncronize some actions in several threads.
//! //!
//! \~russian //! \~russian
//! //!Этот класс используется как событийный механизм между потоками.
//! Один поток ждёт некоторого события и другой его отправляет, разблокируя первый.
//! Это полезно для синхронизации действий в нескольких потоках.
//! //!
//! \~english \section PIThreadNotifier_sec1 Usage //! \~english \section PIThreadNotifier_sec1 Usage
//! \~russian \section PIThreadNotifier_sec1 Использование //! \~russian \section PIThreadNotifier_sec1 Использование
//! \~english //! \~\code
//! PIThreadNotifier notifier;
//! PITimeMeasurer time;
//! //!
//! \~russian //! class Worker: public PIThread {
//! PIOBJECT_SUBCLASS(Worker, PIThread)
//! public:
//! Worker(const PIString & n) {
//! setName(n);
//! }
//! void run() override {
//! piCoutObj << (int)time.elapsed_m() << "wait ...";
//! notifier.wait();
//! piCoutObj << (int)time.elapsed_m() << "done";
//! };
//! };
//! //!
//! //!
//! int main(int argc, char * argv[]) {
//! PIVector<Worker*> workers;
//!
//! // create 2 threads
//! for (auto n: {"first ", "second"})
//! workers << new Worker(n);
//!
//! // start them
//! for (auto * w: workers)
//! w->startOnce();
//!
//! piMSleep(500);
//! notifier.notifyOnce(); // notify one of them after 500 ms
//! piMSleep(500);
//! notifier.notifyOnce(); // notify one of them after 1000 ms
//!
//! for (auto * w: workers)
//! w->waitForFinish();
//! }
//!
//! // [Worker "first "] 0 wait ...
//! // [Worker "second"] 0 wait ...
//! // [Worker "second"] 500 done
//! // [Worker "first "] 1000 done
//! \endcode
//! \} //! \}

View File

@@ -43,8 +43,8 @@ public:
virtual ~PIThreadPoolLoop(); virtual ~PIThreadPoolLoop();
//! \~english Set threads function to "f" with format [ ](int){ ... } //! \~english Set threads function to [lambda expression](https://en.cppreference.com/w/cpp/language/lambda) "f" with format [ ](int){ ... }
//! \~russian Устанавливает функцию потоков на "f" в формате [ ](int){ ... } //! \~russian Устанавливает функцию потоков на [лямбда-выражение](https://ru.cppreference.com/w/cpp/language/lambda) "f" в формате [ ](int){ ... }
void setFunction(std::function<void(int)> f); void setFunction(std::function<void(int)> f);
//! \~english Wait for all threads stop //! \~english Wait for all threads stop

View File

@@ -49,7 +49,7 @@
//! \~russian \section PITimer_sec1 Варианты уведомления //! \~russian \section PITimer_sec1 Варианты уведомления
//! \~english //! \~english
//! Notify variants: //! Notify variants:
//! * "slot" - static function with format void func(void * data, int delimiter) or lambda-function; //! * "slot" - static function with format void func(void * data, int delimiter) or [lambda expression](https://en.cppreference.com/w/cpp/language/lambda);
//! * event - \a tickEvent(); //! * event - \a tickEvent();
//! * virtual function - \a tick(). //! * virtual function - \a tick().
//! //!
@@ -58,7 +58,7 @@
//! All these variants are equivalent, use most applicable. //! All these variants are equivalent, use most applicable.
//! \~russian //! \~russian
//! Варианты уведомления: //! Варианты уведомления:
//! * "slot" - статический метод в формате void func(void * data, int delimiter) или лямбда-функция; //! * "slot" - статический метод в формате void func(void * data, int delimiter) или [лямбда-выражение](https://ru.cppreference.com/w/cpp/language/lambda);
//! * event - \a tickEvent(); //! * event - \a tickEvent();
//! * виртуальный метод - \a tick(). //! * виртуальный метод - \a tick().
//! //!

View File

@@ -48,6 +48,7 @@ const char help_string[] =
"You can override ID with PIMETA(id=<ID>). If in class or struct\n" "You can override ID with PIMETA(id=<ID>). If in class or struct\n"
"PIMETA(simple-stream) presence, then variables stored/restored\n" "PIMETA(simple-stream) presence, then variables stored/restored\n"
"with simple << and >> operators.\n" "with simple << and >> operators.\n"
"If PIMETA(no-stream) presence, then class or struct ignored.\n"
"\n" "\n"
"-G (Getter functions)\n" "-G (Getter functions)\n"
"Generate anonymous access methods for member typenames and values.\n" "Generate anonymous access methods for member typenames and values.\n"