A Simple C++ Plugin System -- Interface Between Plugin and Main Application

原创 2016年10月10日 23:54:04

Abstract

前人已有A Simple C++ Plugin System Summary,本文将处理,如何实现插件与主程序之间的接口

Introduction

支持任意的命令:插件的基本类往往只提供少数接口,因此有必要将一个接口转换为多个接口。下面的字符串接口即实现了这一功能。
数据共享,包括以下
1. 主程序中访问,修改插件参数,参见下面的指针类接口。
2. 插件访问,修改主程序参数,需要在插件的初始化函数中提供主程序引用参数,实测可行。
3. 插件A访问,修改插件B,插件C……中的参数(对插件A的开发者要求很高,插件B,插件C要提供接口供插件A调用,如插件A直接调用插件B,C,但这有时不太现实,如果插件B,C只能有一个实例。这样的话,要求主程序提供一套插件回调机制,插件A通过主程序回调其它插件)。

Related Work

字符串接口

The method we’ve been using recently is actually quite simple. The plugin implements a onCommand method which accepts arbitrary commands from the application. The advantage of using this type of interface is that you’re able to implement almost any kind of functionality without having to add new methods and break the API each time you roll out a new feature. Obviously it doesn’t have it be this simple, but you get the idea!

Command nodes (see code below) are namespaced using a REST style interface like so resource:action, and the data buffer contains either a JSON encoded message which for representing and converting to an STL container such a std::vector or std::map to pass to the internal API, or it may just be a raw data buffer that can be used directly.

bool onCommand(const char* node, const char* data, unsigned int size)
{    
    try {
        // Handle a JSON encoded options hash
        if (strcmp(node, "options:set") == 0) {   
            json::Value root;
            json::Reader reader;
            if (!reader.parse(data, size, root))          
                throw std::runtime_error("Invalid JSON format: " + reader.getFormatedErrorMessages());

            // Do something with JSON data here...
        }

        // Handle raw file data
        else if (strcmp(node, "file:write") == 0) {
            std::string path("test.bin");
            std::ofstream ofs(path, std::ios::out|std::ios::binary);
            if (!ofs.is_open())
                throw std::runtime_error("Cannot write to output file: " + path);
            ofs.write(data, size);
            ofs.close();
        }

        // Handle unknown commands
        else throw std::runtime_error("Unknown command");
    }
    catch (std::exception& exc) {
        // Catch exceptions here and return false.
        // You could set a lastError string here which is exposed to
        // the application that returns the error message as a char*.
        // See the full example for details.
        std::cerr << "Command error: " << exc.what() << std::endl;
        return false;
    }
    return true;
}

指针类接口

通过指针返回符号(函数或者成员变量),注意typedef void* gpointer; 这样,通过void * 可以返回任意数据类型,无论是函数还是成员变量。

/* the function signature for 'say_hello' */
typedef void (* SayHelloFunc) (const char *message);

gboolean
just_say_hello (const char *filename, GError **error)
{
  SayHelloFunc  say_hello;
  GModule      *module;

  module = g_module_open (filename, G_MODULE_BIND_LAZY);
  if (!module)
    {
      g_set_error (error, FOO_ERROR, FOO_ERROR_BLAH,
                   "%s", g_module_error ());
      return FALSE;
    }

  if (!g_module_symbol (module, "say_hello", (gpointer *)&say_hello))
    {
      g_set_error (error, SAY_ERROR, SAY_ERROR_OPEN,
                   "%s: %s", filename, g_module_error ());
      if (!g_module_close (module))
        g_warning ("%s: %s", filename, g_module_error ());
      return FALSE;
    }

  if (say_hello == NULL)
    {
      g_set_error (error, SAY_ERROR, SAY_ERROR_OPEN,
                   "symbol say_hello is NULL");
      if (!g_module_close (module))
        g_warning ("%s: %s", filename, g_module_error ());
      return FALSE;
    }

  /* call our function in the module */
  say_hello ("Hello world!");

  if (!g_module_close (module))
    g_warning ("%s: %s", filename, g_module_error ());
  return TRUE;
 }

[快捷键接口]

类似Atom,在插件中加入信号处理,这样可以通过快捷键调用插件中的函数。

[流水线接口]

所有插件仅处理一种类型的数据,如矩阵。参见orange 实现机制 不过这是python,但原则上c++也可以这么做。但势必会麻烦一些,毕竟python是脚本语言。

[文件接口]

一个很直接的想法,将数据输出到系统文件中,然后让别的程序处理,最后同步即可。

Implement

实现插件访问,修改主程序中的数据DataDriveMain,使其成员变量frameNum增加1.

test4_main.cpp

#include "DataDriveFunctions.h"
#include "DataDriveMain.h"
#include "yzbx_config.h"
#include <dlfcn.h>

using namespace DataDrive ;
typedef Base* create_t(std::shared_ptr<DataDriveMain> data);
typedef void destroy_t(Base*);
int main(){
  using std::cout;
    using std::cerr;

    // load the dll library
    void* dll = dlopen("./test4.so", RTLD_LAZY);
    if (!dll) {
        cerr << "Cannot load library: " << dlerror() << '\n';
        return 1;
    }

    // reset errors
    dlerror();

    // load the symbols
    create_t* create_dll = (create_t*) dlsym(dll, "create");
    const char* dlsym_error = dlerror();
    if (dlsym_error) {
        cerr << "Cannot load symbol create: " << dlsym_error << '\n';
        return 1;
    }

    destroy_t* destroy_dll = (destroy_t*) dlsym(dll, "destroy");
    dlsym_error = dlerror();
    if (dlsym_error) {
        cerr << "Cannot load symbol destroy: " << dlsym_error << '\n';
        return 1;
    }

    // create an instance of the class
    std::shared_ptr<DataDriveMain> data=std::make_shared<DataDriveMain>(DataDriveMain("/home/yzbx/config/surveillance-video-system.ini"));
    Base* base = create_dll(data);

    // use the class
        cout << "The frameNum is: " << data->frameNum << '\n';
    base->run();
    cout << "The frameNum is: " << data->frameNum << '\n';
    // destroy the class
    destroy_dll(base);

    // unload the dll library
    dlclose(dll);
}

test4_dll.cpp

#include "DataDriveFunctions.h"
#include "DataDriveMain.h"
#include "yzbx_config.h"
using namespace DataDrive ;

 class DllTest:public Base{
public:
    GET_CLASS_NAME
    DllTest(std::shared_ptr<DataDriveMain> data):Base(data){}
    bool run();
};

bool DllTest::run(){
  data->frameNum++;
  return true;
};

extern "C" Base * create(std::shared_ptr<DataDriveMain> data){
  return new DllTest(data);
};

extern "C" void destroy(Base * p){
  delete p;
};

build.sh

g++ -std=c++14 -fPIC -I/home/yzbx/git/surveillance-video-system/objectTracking/src/objectTracking/yzbxLib `pkg-config --cflags Qt5Core` test4_dll.cpp -shared -o test4.so  `pkg-config --libs Qt5Core` -L/home/yzbx/build/build-objectTracking-Desktop_Qt_5_6_0_GCC_64bit-Debug/yzbxLib -lyzbxLib

g++ -std=c++14 -fPIC -I/home/yzbx/git/surveillance-video-system/objectTracking/src/objectTracking/yzbxLib `pkg-config --cflags Qt5Core` test4_main.cpp -o test4  -L/home/yzbx/build/build-objectTracking-Desktop_Qt_5_6_0_GCC_64bit-Debug/yzbxLib -lyzbxLib `pkg-config --libs Qt5Core` `pkg-config --libs bgslibrary` `pkg-config --libs opencv` -lboost_filesystem -lboost_program_options -lboost_system -ldl

output

(env2) $: cd -
/home/yzbx/test/plugin-framework/dlopen
(env2) $: ls
a.out      hello.so     test1-libm-so    test3_triangle.cpp  triangle.so
bin        Jamroot      test2_hello.cpp  test4
build.sh   polygon.hpp  test2_main.cpp   test4_dll.cpp
config     test1        test3_main       test4_main.cpp
error.log  test1.c      test3_main.cpp   test4.so
(env2) $: ./test4
The frameNum is: 0
The frameNum is: 1
(env2) $: 
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Communication Between Hosting System and Its Plugin Applications(Cross Domains)

The first rendered HTML code in mother system is: if(!window.hasInitShop88Addon){ in...

RAKNET Plugin Interface 2 - 工作原理

1. 概述 PluginInterface2是一个类接口PluginInterface2是一套预处理函数集使用::AttachPlugin()函数注册到RakPeerInterface或者Pac...
  • rsspub
  • rsspub
  • 2013年05月29日 10:32
  • 811

Qt:Application failed to start because platform plugin “windows” is missing.

今天,在新建一个Qt App的时候,编译新的工程就出现以下错误: 于是,我就一脸懵逼了,前几天还可以,今天砸门大姨妈了···,于是开始百度这个问题,发现他们说 plugin/platforms/下缺...

Different Between An Interface and An Abstract Class

What is the different between an interface and an abstract class in Java? It is best to start a...

Android Application plugin

在网易云阅读App上看到了插件管理功能,刚好自己也需要以插件的模式来扩展已有的功能,于是研究了一下,下面是一张网易云阅读App提供的插件模式,只需下载相应的插件就扩展了相应的功能,非常方便。 ...
  • Tibib
  • Tibib
  • 2013年06月29日 16:30
  • 3460

Gstreamer插件教程2.9—编写一个插件(Writing a Plugin):构建一个测试应用(Building a Test Application)

你将经常想用尽可能小的设置来测试你新写的插件。通常,gst-launch-1.0是用于测试插件的第一步。如果你还未在Gstreamer的搜索目录上安装你的插件,你将需要设置插件路径。或者设置GST_P...

ORACLE EBS出现In order to access this application, you must install the J2SE Plugin version 1.6.0_07.

在操作oracle ebs时点击供应商合并

RED5 Plugin 及 RED5 Application在Eclipse中的调试

1、  安装RED5 Plugin,根据你的Eclipse版本,选择安装对应的PLUGIN,地址:http://www.red5.org/red5-ide-plugin/ 2、  配置好PLUGIN...

“插件(application/x-vlc-plugin)不受支持”NPAPI和PPAPI的问题

最近做一个前端的项目,项目需要引用VLC浏览器插件,javascript在IE、Firefox等浏览器上都没有问题,唯独在Chrome(谷歌)浏览器中插件不能被会支持。     不断更换Chro...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:A Simple C++ Plugin System -- Interface Between Plugin and Main Application
举报原因:
原因补充:

(最多只允许输入30个字)