c++ 插件化开发 – 知乎

c++ 插件化开发

图片[1]就去ID网c++ 插件化开发 – 知乎就去ID网97ID网

沃德锅

传统方式

图片[2]就去ID网c++ 插件化开发 – 知乎就去ID网97ID网

插件开发新方法

图片[3]就去ID网c++ 插件化开发 – 知乎就去ID网97ID网

插件管理器 plugin manager 主程序去管理插件。

  • 问题分析
    • 为什么需要依赖一个插件接口?

因为不能创建插件对象

需要依赖接口与多态才能创建插件对象

POCO_BEGIN_MANIFSET(IPlugin)
    POCO_EXPORT_CLASS(PluginA)
    POCO_EXPORT_CLASS(PluginB)
POCO_END_MANIFEST

template<typename Derived, typename Base>
void RegisterPlugin(const std::string &class_name, const std::string &base_class_name)
{
   AbstractMetaObject<Base> *new_factory = new 
            MetaObject<Derived, Base>(class_name, base_class_name);
   //....
}

回到c++

依赖接口和多态才能创建插件对象。

插件化开发新方法的实现

  • 新插件就是一个普通的动态库,不依赖任何接口。
  1. 1. 在动态库中注册需要增加的服务方法。
  2. 2. 在注册插件方法时做类型擦除
  3. 3. 在调用时还原类型完成新增服务的调用。

rpc服务扩展插件:

图片[4]就去ID网c++ 插件化开发 – 知乎就去ID网97ID网

操作动态链接库

图片[5]就去ID网c++ 插件化开发 – 知乎就去ID网97ID网

编译期擦除:

图片[6]就去ID网c++ 插件化开发 – 知乎就去ID网97ID网

图片[7]就去ID网c++ 插件化开发 – 知乎就去ID网97ID网

擦除任意类型的函数

图片[8]就去ID网c++ 插件化开发 – 知乎就去ID网97ID网

查找插件函数并调用(类型还原)

图片[9]就去ID网c++ 插件化开发 – 知乎就去ID网97ID网

插件管理主函数:

图片[10]就去ID网c++ 插件化开发 – 知乎就去ID网97ID网

插件custom

图片[11]就去ID网c++ 插件化开发 – 知乎就去ID网97ID网

传入路径

call_in_so_ 调用。

代码地址:https://github.com/zhuzhenxxx/plugincpp.git

除了boost有通用的库加载函数。

还有一些开源库:

https://pocoproject.org/slides/120-SharedLibraries.pdf

比如POCO

haredLibrary的功能一句话可以概括,在运行时动态的加载库和库内的类。也就是说SharedLibrary提供了一个架构或者是约定,供库使用方和库提供方使用。只要满足了模块约定,就可以快速实现调用。

对于库的调用来说,导出函数和导出类是基本的功能,windows和linux下具是如此,因此SharedLibrary也必须实现此功能。

1.1 导出函数

先来看一个例子,说明导出函数是如何使用的。

对于库提供方而言:

// TestLibrary.cpp
#include <iostream>
#if defined(_WIN32)
#define LIBRARY_API __declspec(dllexport)
#else
#define LIBRARY_API
#endif
extern "C" void LIBRARY_API hello();
void hello()
{
       std::cout << "Hello, world!" << std::endl;
}

对于使用方而言:

// LibraryLoaderTest.cpp
#include "Poco/SharedLibrary.h"
using Poco::SharedLibrary;
typedef void (*HelloFunc)(); // function pointer type
int main(int argc, char** argv)
{
    std::string path("TestLibrary");
    path.append(SharedLibrary::suffix()); // adds ".dll" or ".so"
    SharedLibrary library(path); // will also load the library
    HelloFunc func = (HelloFunc) library.getSymbol("hello");
    func();
    library.unload();

    return 0;
}

上述步骤,和调用普通的window dll和linux so文件步骤是如此的类似:第一步加载库文件,第二步获取库中API的函数地址,第三步运行函数。不同是所有的功能从操作系统提供的API变成了封装类SharedLibrary的类操作。

1.2 导出类

再来看一个例子,说明SharedLibrary模块中类是如何导出并被使用的。

对于库提供方:

.h文件

// AbstractPlugin.h
//
// This is used both by the class library and by the application.
#ifndef AbstractPlugin_INCLUDED
#define AbstractPlugin_INCLUDED
class AbstractPlugin
{
public:
   AbstractPlugin();
   virtual ~AbstractPlugin();
   virtual std::string name() const = 0;
};
#endif // AbstractPlugin.h

cpp文件

// AbstractPlugin.cpp
//
// This is used both by the class library and by the application.
#include "AbstractPlugin.h"
AbstractPlugin::AbstractPlugin()
{}
AbstractPlugin::~AbstractPlugin()
{}
 
 
// PluginLibrary.cpp
#include "AbstractPlugin.h"
#include "Poco/ClassLibrary.h"
#include <iostream>
class PluginA: public AbstractPlugin
{
public:
	std::string name() const
	{
		return "PluginA";
	}
};
class PluginB: public AbstractPlugin
{
public:
	std::string name() const
	{
		return "PluginB";
	}
};
 
POCO_BEGIN_MANIFEST(AbstractPlugin)
POCO_EXPORT_CLASS(PluginA)
POCO_EXPORT_CLASS(PluginB)
POCO_END_MANIFEST
 
// optional set up and clean up functions
void pocoInitializeLibrary()
{
	std::cout << "PluginLibrary initializing" << std::endl;
}
void pocoUninitializeLibrary()
{
	std::cout << "PluginLibrary uninitializing" << std::endl;
}

对于使用方来说:

// main.cpp
#include "Poco/ClassLoader.h"
#include "Poco/Manifest.h"
#include "AbstractPlugin.h"
#include <iostream>
typedef Poco::ClassLoader<AbstractPlugin> PluginLoader;
typedef Poco::Manifest<AbstractPlugin> PluginManifest;
int main(int argc, char** argv)
{
	PluginLoader loader;
	std::string libName("PluginLibrary");
	libName += Poco::SharedLibrary::suffix(); // append .dll or .so
	loader.loadLibrary(libName);
	PluginLoader::Iterator it(loader.begin());
	PluginLoader::Iterator end(loader.end());
	for (; it != end; ++it)
	{
	    std::cout << "lib path: " << it->first << std::endl;
	    PluginManifest::Iterator itMan(it->second->begin());
	    PluginManifest::Iterator endMan(it->second->end());
	    for (; itMan != endMan; ++itMan)
		std::cout << itMan->name() << std::endl;
	    }
	AbstractPlugin* pPluginA = loader.create("PluginA");
	AbstractPlugin* pPluginB = loader.create("PluginB");
	std::cout << pPluginA->name() << std::endl;
	std::cout << pPluginB->name() << std::endl;
	loader.classFor("PluginA").autoDelete(pPluginA);
	delete pPluginB;
	loader.unloadLibrary(libName);
	return 0;
}

编辑于 2021-11-13 21:21

C / C++

C++

软件开发

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    请登录后查看评论内容