Qt5.4如何基于插件创建应用程序?

时间:2015-07-14 17:54:12

标签: c++ qt qt5.4

我在QT 5.4中有一个应用程序但是当我需要包含一个新功能时,我需要重新编译所有应用程序,这需要一些时间,我需要知道如何创建或修改我的应用程序以使用我创建的插件。

2 个答案:

答案 0 :(得分:1)

基于插件的架构需要二进制兼容且稳定的接口。有了这些,完整项目重新编译应该花费与重新编译单个插件相同的时间。

最有可能的是,您的代码中存在相互依赖性,无论如何都会妨碍维护二进制兼容性 - 如果不这样做,您的更改将会进行足够的本地化,以便重新编译只会触及几个文件。

您正在尝试做的是提出解决错误问题的方法。修复代码的结构,重新编译时间将会减少。不需要插件。

答案 1 :(得分:0)

有很多选择;一个流行的包括使用实现定义良好的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]);
  }
}

编译并链接到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&#39; s ctypes库,或者Matlab&#39; loadlibrary) 。我也可以在Fortran中编写插件,然后将其封装在与API兼容的接口中。

最后:请注意,这与QT完全无关(尽管QT可能提供独立于平台的共享库加载器;我不知道)。这只是人们为定制提供钩子的常见方式。