Qt5.4 如何创建基于插件的应用程序?
Qt5.4 How can I create an app based in plugins?
我在 QT 5.4 中有一个应用程序,但当我需要包含新功能时,我需要重新编译所有应用程序,这需要时间,我需要知道如何创建或修改我的应用程序以使用我创建的插件。
基于插件的架构需要二进制兼容和稳定的接口。一旦你有了这些,一个完整的项目重新编译应该花费和重新编译一个插件一样多的时间。
最有可能的是,您的代码中存在相互依赖关系,无论如何都无法保持二进制兼容性 - 如果没有,您的更改将足够本地化,因此重新编译只会涉及几个文件。
您要做的是想出解决错误问题的方法。修复你的代码结构,你的重新编译时间将会减少。无需插件。
有很多选择;一个流行的方法是使用共享库实现定义明确的 API.
例如。假设这是您要打开以进行自定义的 API:
// pluggin_api.hpp
// This file defines the pluggin interface.
#pragma once
extern "C" { // avoid name mangling
const char* pluggin_name();
void foo(int x);
void bar(int y);
}
然后您的用户(或您自己)将实现此 API 的不同变体,例如:
// pluggin_1.cpp
#include "pluggin_api.hpp"
#include <iostream>
const char* pluggin_name() {
return "Pluggin 1";
}
void foo(int x) {
std::cout << "2 * x = " << 2 * x << std::endl;
}
void bar(int y) {
std::cout << " 3 * y = " << 3 * y << std::endl;
}
和
// pluggin_2.cpp
#include "pluggin_api.hpp"
#include <iostream>
const char* pluggin_name() {
return "Pluggin 2";
}
void foo(int x) {
std::cout << "20 * x = " << 20 * x << std::endl;
}
void bar(int y) {
std::cout << " 30 * y = " << 30 * y << std::endl;
}
这些.cpp文件被编译为共享库;在 Linux 下它看起来像这样:
$ g++ -shared -fPIC -o pluggin_1.so pluggin_1.cpp
$ g++ -shared -fPIC -o pluggin_2.so pluggin_2.cpp
最后,主应用程序可以按名称调用不同的插件:
// main.cpp
#include <iostream>
#include <dlfcn.h> // POSIX --- will work on Linux and OS X, but
// you'll need an equivalent library for Windows
void execute_pluggin(const char* name) {
// declare the signature of each function in the pluggin -- you
// could do this in the header file instead (or another auxiliary
// file)
using pluggin_name_signature = const char*(*)();
using foo_signature = void(*)(int);
using bar_signature = void(*)(int);
// open the shared library
void* handle = dlopen(name, RTLD_LOCAL | RTLD_LAZY);
// extract the functions
auto fun_pluggin_name = reinterpret_cast<pluggin_name_signature>(dlsym(handle, "pluggin_name"));
auto fun_foo = reinterpret_cast<foo_signature>(dlsym(handle, "foo"));
auto fun_bar = reinterpret_cast<bar_signature>(dlsym(handle, "bar"));
// call them
std::cout << "Calling Pluggin: " << fun_pluggin_name() << std::endl;
fun_foo(2);
fun_bar(3);
// close the shared library
dlclose(handle);
}
int main(int argc, char *argv[]) {
for(int k = 1; k < argc; ++k) {
execute_pluggin(argv[k]);
}
}
编译并link使用dl
库:
$ g++ -o main main.cpp -std=c++14 -ldl
和 运行(注意名称前需要 ./
;这与库命名约定和搜索路径有关):
$ ./main ./pluggin_1.so ./pluggin_2.so
Calling Pluggin: Pluggin 1
2 * x = 4
3 * y = 9
Calling Pluggin: Pluggin 2
20 * x = 40
30 * y = 90
我遗漏了太多细节(最重要的是错误管理)。我建议您阅读这本书 API Design for C++ 以找到其他想法(例如使用脚本语言、使用继承、使用模板以及它们的混合)。
我喜欢共享库方法,因为这样我就可以在其他应用程序中使用插件(例如:我可以使用 Python 的 ctypes
库,或 Matlab 的 loadlibrary
) .我还可以用 Fortran 语言编写插件,然后将其包装在与 API.
兼容的接口中
最后:请注意,这与 QT 完全无关(尽管 QT 可能提供独立于平台的共享库加载程序;我不知道)。这只是人们为定制提供钩子的一种常见方式。
Qt 文档提供了有关创建插件以使用 Qt 自己的机制扩展基于 Qt 的应用程序的 'How To'。参见 http://doc.qt.io/qt-5/plugins-howto.html。
它讲了一个高级 API和一个低级 API。您对低级API.
感兴趣
我在 QT 5.4 中有一个应用程序,但当我需要包含新功能时,我需要重新编译所有应用程序,这需要时间,我需要知道如何创建或修改我的应用程序以使用我创建的插件。
基于插件的架构需要二进制兼容和稳定的接口。一旦你有了这些,一个完整的项目重新编译应该花费和重新编译一个插件一样多的时间。
最有可能的是,您的代码中存在相互依赖关系,无论如何都无法保持二进制兼容性 - 如果没有,您的更改将足够本地化,因此重新编译只会涉及几个文件。
您要做的是想出解决错误问题的方法。修复你的代码结构,你的重新编译时间将会减少。无需插件。
有很多选择;一个流行的方法是使用共享库实现定义明确的 API.
例如。假设这是您要打开以进行自定义的 API:
// pluggin_api.hpp
// This file defines the pluggin interface.
#pragma once
extern "C" { // avoid name mangling
const char* pluggin_name();
void foo(int x);
void bar(int y);
}
然后您的用户(或您自己)将实现此 API 的不同变体,例如:
// pluggin_1.cpp
#include "pluggin_api.hpp"
#include <iostream>
const char* pluggin_name() {
return "Pluggin 1";
}
void foo(int x) {
std::cout << "2 * x = " << 2 * x << std::endl;
}
void bar(int y) {
std::cout << " 3 * y = " << 3 * y << std::endl;
}
和
// pluggin_2.cpp
#include "pluggin_api.hpp"
#include <iostream>
const char* pluggin_name() {
return "Pluggin 2";
}
void foo(int x) {
std::cout << "20 * x = " << 20 * x << std::endl;
}
void bar(int y) {
std::cout << " 30 * y = " << 30 * y << std::endl;
}
这些.cpp文件被编译为共享库;在 Linux 下它看起来像这样:
$ g++ -shared -fPIC -o pluggin_1.so pluggin_1.cpp
$ g++ -shared -fPIC -o pluggin_2.so pluggin_2.cpp
最后,主应用程序可以按名称调用不同的插件:
// main.cpp
#include <iostream>
#include <dlfcn.h> // POSIX --- will work on Linux and OS X, but
// you'll need an equivalent library for Windows
void execute_pluggin(const char* name) {
// declare the signature of each function in the pluggin -- you
// could do this in the header file instead (or another auxiliary
// file)
using pluggin_name_signature = const char*(*)();
using foo_signature = void(*)(int);
using bar_signature = void(*)(int);
// open the shared library
void* handle = dlopen(name, RTLD_LOCAL | RTLD_LAZY);
// extract the functions
auto fun_pluggin_name = reinterpret_cast<pluggin_name_signature>(dlsym(handle, "pluggin_name"));
auto fun_foo = reinterpret_cast<foo_signature>(dlsym(handle, "foo"));
auto fun_bar = reinterpret_cast<bar_signature>(dlsym(handle, "bar"));
// call them
std::cout << "Calling Pluggin: " << fun_pluggin_name() << std::endl;
fun_foo(2);
fun_bar(3);
// close the shared library
dlclose(handle);
}
int main(int argc, char *argv[]) {
for(int k = 1; k < argc; ++k) {
execute_pluggin(argv[k]);
}
}
编译并link使用dl
库:
$ g++ -o main main.cpp -std=c++14 -ldl
和 运行(注意名称前需要 ./
;这与库命名约定和搜索路径有关):
$ ./main ./pluggin_1.so ./pluggin_2.so
Calling Pluggin: Pluggin 1
2 * x = 4
3 * y = 9
Calling Pluggin: Pluggin 2
20 * x = 40
30 * y = 90
我遗漏了太多细节(最重要的是错误管理)。我建议您阅读这本书 API Design for C++ 以找到其他想法(例如使用脚本语言、使用继承、使用模板以及它们的混合)。
我喜欢共享库方法,因为这样我就可以在其他应用程序中使用插件(例如:我可以使用 Python 的 ctypes
库,或 Matlab 的 loadlibrary
) .我还可以用 Fortran 语言编写插件,然后将其包装在与 API.
最后:请注意,这与 QT 完全无关(尽管 QT 可能提供独立于平台的共享库加载程序;我不知道)。这只是人们为定制提供钩子的一种常见方式。
Qt 文档提供了有关创建插件以使用 Qt 自己的机制扩展基于 Qt 的应用程序的 'How To'。参见 http://doc.qt.io/qt-5/plugins-howto.html。
它讲了一个高级 API和一个低级 API。您对低级API.
感兴趣