无法跳转到建设银行网站,网站中加入企业qq,游戏网页设计模板图片,wordpress 主机优化1. 概述 Pluma 是一个用 C 开发的可用于管理插件的开源架构#xff0c;其官网地址为#xff1a;http://pluma-framework.sourceforge.net/。该架构是个轻量级架构#xff0c;非常易于理解。 Pluma 架构有以下基本概念#xff1a; 1#xff09;插件的外在行为体现为一个…1. 概述 Pluma 是一个用 C 开发的可用于管理插件的开源架构其官网地址为http://pluma-framework.sourceforge.net/。该架构是个轻量级架构非常易于理解。 Pluma 架构有以下基本概念 1插件的外在行为体现为一个纯虚类可以叫作插件接口 2继承于同一个插件接口的若干派生类被认为属于同一种插件可以叫作插件类 3每一个插件接口或插件类都有个一一对应的 Provider 类其中插件接口对应的 Provider 类里会定义一个特殊字符串常量PLUMA_PROVIDER_TYPE表示这一类 “插件 Provider” 共同的类型名称而这个类型名称其实就是插件接口的类名字符串。 4多个插件类可以被放入一个插件动态库中而这个动态库文件名不包括后缀部分可以叫作 “插件名”。 5插件机制使用者可以在自己的架构中包含一个 Pluma 管理类该类支持从所指定的位置加载一个或多个插件动态库并将每个插件类对应的 Provider记录进内部的表中。 6插件机制使用者可以在合适时机利用 Pluma 获取内含的插件 Provider并调用某个插件 Provider 的 create () 函数创建出对应的插件对象。 7使用完插件对象后不要忘了 delete 它。 现在我们画一张示意图 2. Pluma 管理类
我们刚刚也说了插件机制使用者可以包含一个 Pluma 管理类。该类继承于 PluginManager 类。
【pluma-1.1/include/pluma/PluginManager.hpp】
class PLUMA_API PluginManager{
public:~PluginManager();bool load(const std::string path);bool load(const std::string folder, const std::string pluginName);int loadFromFolder(const std::string folder, bool recursive false);bool unload(const std::string pluginName);void unloadAll();bool addProvider(Provider* provider);void getLoadedPlugins(std::vectorconst std::string* pluginNames) const;bool isLoaded(const std::string pluginName) const;protected:PluginManager();void registerType(const std::string type, unsigned int version, unsigned int lowestVersion);const std::listProvider** getProviders(const std::string type) const;private:static std::string getPluginName(const std::string path);static std::string resolvePathExtension(const std::string path);private:typedef bool fnRegisterPlugin(Host);typedef std::mapstd::string,DLibrary* LibMap;LibMap libraries; /// Map containing the loaded librariesHost host; /// Host app proxy, holding all providers
}; 从上面的 load () 函数和 loadFromFolder () 函数可以看出插件管理器既允许用户单独加载某个插件动态库也允许批量性加载某个目录下所有的插件动态库。另外值得注意的是getProviders () 函数是 protected 的成员也就是说这套架构是不希望用户直接使用这个 PluginManager 类的即便用了你也拿不到 Provider。正确的做法是使用 PluginManager 的子类Pluma 管理类。 另外上面的成员变量 libraries就是记录所有已加载的插件动态库的映射表。而成员变量 host 则负责记录每个插件类对应的 Provider 信息。之所以被称为 host宿主是针对插件而言的。也就是说插件本身实际上是没资格知道其真实宿主的全貌的它只能访问和它相关的很小一部分数据而已因此 Pluma 将这一小部分数据整理成一个 host 代理供插件使用。 Pluma 管理类的代码截选如下 【pluma-1.1/include/pluma/Pluma.hpp】
class Pluma: public PluginManager{
public:Pluma();templatetypename ProviderTypevoid acceptProviderType();templatetypename ProviderTypevoid getProviders(std::vectorProviderType* providers);
};#include Pluma/Pluma.inl
请大家注意上面代码中最后一行这个 Pluma.hpp 还真是有点手黑偷偷摸摸 #include 了个 Pluma.inl 文件其实展开来就是 acceptProviderType () 和 getProviders () 这两个模板函数的实现。Pluma.inl 文件的内容如下 【pluma-1.1/include/pluma/Pluma.inl】
inline Pluma::Pluma(){// Nothing to do
}templatetypename ProviderType
void Pluma::acceptProviderType(){PluginManager::registerType(ProviderType::PLUMA_PROVIDER_TYPE,ProviderType::PLUMA_INTERFACE_VERSION,ProviderType::PLUMA_INTERFACE_LOWEST_VERSION);
}templatetypename ProviderType
void Pluma::getProviders(std::vectorProviderType* providers){const std::listProvider** lst PluginManager::getProviders(ProviderType::PLUMA_PROVIDER_TYPE);if (!lst) return;providers.reserve(providers.size() lst-size());std::listProvider*::const_iterator it;for (it lst-begin() ; it ! lst-end() ; it)providers.push_back(static_castProviderType*(*it));
} 看到了吧重新定义了个 getProviders ()还搞成一个模板函数在函数体内会反过来通过模板参数进一步得到所涉及的插件 Provider 的 PLUMA_PROVIDER_TYPE 信息这个技巧挺重要。也就是说外界传来的是 vectorProviderType而函数内部可以推断出 ProviderType::PLUMA_PROVIDER_TYPE。将 PLUMA_PROVIDER_TYPE 传入父类的 PluginManager::getProviders () 函数就可以拿到符合所指类型的所有 Provider。 我们画一张 Pluma 简图后面再细说相关细节 同一类插件类会对应一个 ProviderInfo 节点该节点内部的 providers 列表记录着同属一类的若干 Provider。
2.1 Host 代理
【pluma-1.1/include/pluma/Host.hpp】
class PLUMA_API Host{
friend class PluginManager;
friend class Provider;public:bool add(Provider* provider);private:Host();~Host();bool knows(const std::string type) const;unsigned int getVersion(const std::string type) const;unsigned int getLowestVersion(const std::string type) const;void registerType(const std::string type, unsigned int version, unsigned int lowestVersion);const std::listProvider** getProviders(const std::string type) const;void clearProviders();bool validateProvider(Provider* provider) const;bool registerProvider(Provider* provider);void cancelAddictions();bool confirmAddictions();private:struct ProviderInfo{unsigned int version;unsigned int lowestVersion;std::listProvider* providers;};typedef std::mapstd::string, ProviderInfo ProvidersMap;typedef std::mapstd::string, std::listProvider* TempProvidersMap;ProvidersMap knownTypes; /// Map of registered types.TempProvidersMap addRequests; /// Temporarily added providers
};
正如前文所说Host 代理是针对插件而言的。而 Host 只有一个 public 成员函数 add ()说明其主要对外行为就是让插件将对应的 provider 注册进 Host。
3. 插件类和其对应的 Provider 类 在说了一大堆插件管理类代码后现在终于要开始说插件部分了。前文已经说过插件的外在行为体现为一个纯虚类可以叫作插件接口。我们现在就以 Pluma 源码中给出的例子为准来说明一些细节。
3.1 Warrior 接口和 WarriorProvider 类 Pluma 中的插件接口例子是 Warrior其源码截选如下 【pluma-1.1/example/src/interface/Warrior.hpp】
#include Pluma/Pluma.hpp
class Warrior{
public:virtual std::string getDescription() 0;// (...)
};
PLUMA_PROVIDER_HEADER(Warrior);
这个接口里只象征性的写了一个成员函数 getDescription ()大家明白意思即可。 需要注意的是类定义之后的那句 PLUMA_PROVIDER_HEADER这个宏负责定义和插件接口对应的 Provider 类。相关的宏定义如下 【pluma-1.1/include/pluma/Pluma.hpp】
#define PLUMA_PROVIDER_HEADER(TYPE)\
PLUMA_PROVIDER_HEADER_BEGIN(TYPE)\
virtual TYPE* create() const 0;\
PLUMA_PROVIDER_HEADER_END#define PLUMA_PROVIDER_HEADER_BEGIN(TYPE)\
class TYPE##Provider: public pluma::Provider{\
private:\friend class pluma::Pluma;\static const unsigned int PLUMA_INTERFACE_VERSION;\static const unsigned int PLUMA_INTERFACE_LOWEST_VERSION;\static const std::string PLUMA_PROVIDER_TYPE;\std::string plumaGetType() const{ return PLUMA_PROVIDER_TYPE; }\
public:\unsigned int getVersion() const{ return PLUMA_INTERFACE_VERSION; }#define PLUMA_PROVIDER_HEADER_END };
基于这些宏定义我们可以将 PLUMA_PROVIDER_HEADER (Warrior) 展开为
class WarriorProvider: public pluma::Provider{
private:friend class pluma::Pluma;static const unsigned int PLUMA_INTERFACE_VERSION;static const unsigned int PLUMA_INTERFACE_LOWEST_VERSION;static const std::string PLUMA_PROVIDER_TYPE;std::string plumaGetType() const{ return PLUMA_PROVIDER_TYPE; }
public:unsigned int getVersion() const{ return PLUMA_INTERFACE_VERSION; }virtual Warrior* create() const 0;
};
代码很清晰为 Warrior 接口声明一个配套的 WarriorProvider 类。这个类里包含着重要的 PLUMA_PROVIDER_TYPE 常量以及最关键的 create () 函数。 Warrior 的实现文件更加简单 【pluma-1.1/example/src/interface/Warrior.cpp】
#include Warrior.hpp
PLUMA_PROVIDER_SOURCE(Warrior, 1, 1);
也在使用宏展开宏后可见
const std::string WarriorProvider::PLUMA_PROVIDER_TYPE Warrior;
const unsigned int WarriorProvider::PLUMA_INTERFACE_VERSION 1;
const unsigned int WarriorProvider::PLUMA_INTERFACE_LOWEST_VERSION 1;
因为 Warrior 本身是个纯虚类所以 WarriorProvider 里也不用实现 create () 函数。
3.2 Warrior 派生类和派生 Provider 在 pluma 源码的例子中提供了三个 Warrior 派生类SimpleWarrior、Eagle 和 Jaguar。默认的是 SimpleWarrior它被集成进 example/src/host 目录。也就是说即便我们一个额外的插件库都不提供示例至少还可以使用 SimpleWarrior。而 Eagle 和 Jaguar 则位于 example/src/plugin 目录可以打包进一个插件动态库。
【pluma-1.1/example/src/host/SimpleWarrior.hpp】
#include Warrior.hpp
class SimpleWarrior: public Warrior{
public:std::string getDescription(){return Commoner: leaded by calpoleque;}
};
PLUMA_INHERIT_PROVIDER(SimpleWarrior, Warrior);
前文我们已经看到对于插件接口Warrior来说用到的宏是 PLUMA_PROVIDER_HEADER(Warrior)现在针对实际插件类SimpleWarrior会用到另一个宏 PLUMA_INHERIT_PROVIDER(SimpleWarrior, Warrior)。这个宏的定义如下 【pluma-1.1/include/pluma/Pluma.hpp】
#define PLUMA_INHERIT_PROVIDER(SPECIALIZED_TYPE, BASE_TYPE)\
class SPECIALIZED_TYPE##Provider: public BASE_TYPE##Provider{\
public:\BASE_TYPE * create() const{ return new SPECIALIZED_TYPE (); }\
};
展开后可见
class SimpleWarriorProvider: public WarriorProvider{
public:Warrior * create() const { return new SimpleWarrior (); }
};
很简单就是在完成 Provider 的核心使命提供一个创建插件类对象的 create () 函数。与 SimpleWarriorProvider 类似另外两个 Warrior 派生类 Eagle 和 Jaguar 大体也是这么写的。示意图如下 在研究 Pluma 所给示例时我已事先将 Pluma 封装成静态库了现在要把 Eagle 和 Jaguar 编译并封装成一个动态库就需要链接 Pluma 静态库除此之外还需要编译其他一些辅助文件列举如下 1Connector.cpp 2dllmain.cpp 3Eagle.hpp 4Jaguar.hpp 5Warrior.cpp 其中 Connector.cpp 文件是插件动态库向外界 Host 注册自己所有 Provider 的地方。它必须实现一个 connect () 函数代码截选如下
#include Pluma/Connector.hpp
#include Eagle.hpp
#include Jaguar.hppPLUMA_CONNECTOR
bool connect(pluma::Host host){host.add( new EagleProvider() );host.add( new JaguarProvider() );return true;
} 我们先不要着急分析上面的 connect () 动作可以先跟着我看看插件的加载流程后文我们就会知道connect () 只是加载流程的一环而已。
4. 插件加载流程 我们看一下 Pluma 架构所给例子的 main () 函数就可以了解插件的加载流程了
int main()
{pluma::Pluma pluma;pluma.acceptProviderTypeWarriorProvider(); // 表明用户感兴趣东西添加ProviderInfopluma.load(plugins, PlumaDemoWarriorPlugin); // 加载动作向ProviderInfo里加料std::vectorWarriorProvider* providers;pluma.getProviders(providers);std::vectorWarriorProvider*::iterator it;for (it providers.begin(); it ! providers.end(); it) {Warrior* warrior (*it)-create();std::cout warrior-getDescription() std::endl;delete warrior;}pluma.unloadAll();std::cout Press any key to exit;std::cin.ignore(10000, \n);return 0;
}
其中和加载插件相关的句子主要就是 pluma.acceptProviderType 和 pluma.load 两句了。前者主要负责在 Host 的 knownTypes 映射表中添加一个 ProviderInfo 节点后者负责加载插件动态库并将动态库里匹配的 Provider 指针记入 ProviderInfo 节点。
4.1 pluma.acceptProviderType() 我们先说 pluma.acceptProviderType 一句。在前文介绍 Pluma.inl 文件的内容时我们已经看到一个叫作 acceptProviderType 的模板函数了当时没有细说现在我把它的代码再贴一下 【pluma-1.1/include/pluma/Pluma.inl】
templatetypename ProviderType
void Pluma::acceptProviderType(){PluginManager::registerType(ProviderType::PLUMA_PROVIDER_TYPE,ProviderType::PLUMA_INTERFACE_VERSION,ProviderType::PLUMA_INTERFACE_LOWEST_VERSION);
}
里面调用的是 PluginManager 基类的 registerType () 函数。我们前文主要关心的是 PLUMA_PROVIDER_TYPE现在再说一下后两个参数。PLUMA_INTERFACE_VERSION 表示管理器当前应该使用的插件接口的版本因为我们不能确定更高版本的插件接口会不会增加或删除成员函数所以这个值其实是个限定值如果后续用户尝试加载更高版本的插件那么是无法通过校验的。第三个参数 PLUMA_INTERFACE_LOWEST_VERSION 则是限定最低值如果尝试加载比这个值更低版本的插件肯定也是不会通过的。 在刚刚看到的 main () 函数里是这样写的
pluma.acceptProviderTypeWarriorProvider();
也就是说Pluma 插件管理器对 Warrior 接口对应的 WarriorProvider 类感兴趣。而当初定义 Warrior 时在 Warrior.cpp 文件里的确指明了 WarriorProvider 能限定的当前版本号和最低版本号
PLUMA_PROVIDER_SOURCE(Warrior, 1, 1); 这些类型信息、版本号限定信息都会被注册在 Host 的 knownTypes 映射表中每种接口类型对应一个 ProviderInfo 节点。注册动作的代码如下 【pluma-1.1/src/pluma/PluginManager.cpp】
void PluginManager::registerType(const std::string type, unsigned int version,unsigned int lowestVersion){host.registerType(type, version, lowestVersion);
}
【pluma-1.1/src/pluma/Host.cpp】
void Host::registerType(const std::string type, unsigned int version, unsigned int lowestVersion){if (!knows(type)){ProviderInfo pi;pi.version version;pi.lowestVersion lowestVersion;knownTypes[type] pi;}
}
当然新加的 ProviderInfo 节点的 providers 列表是个空列表待后续再添加 Provider * 内容。
4.2 pluma.load() 接着我们继续看 main () 函数里调用的 pluma.load ()其实调用的是其父类 PluginManager 的 load ()。相关代码截选如下 【pluma-1.1/src/pluma/PluginManager.cpp】
bool PluginManager::load(const std::string path){std::string plugName getPluginName(path);std::string realPath resolvePathExtension(path);DLibrary* lib DLibrary::load(realPath);......fnRegisterPlugin* registerFunction;registerFunction reinterpret_castfnRegisterPlugin*(lib-getSymbol(connect));......if (!registerFunction(host)){......return false;}if (host.confirmAddictions())libraries[plugName] lib;else{......return false;}return true;
}
可以看到一开始就在着手加载动态库并调用动态库里的 connect () 函数。前文我们实际上已经列举过示例代码里的 connect () 函数了现在再贴一次
PLUMA_CONNECTOR
bool connect(pluma::Host host){host.add( new EagleProvider() );host.add( new JaguarProvider() );return true;
} 前文在阐述到 connect () 时暂时没有细说 add () 动作现在我们来看看它的代码 【pluma-1.1/src/pluma/Host.cpp】
bool Host::add(Provider* provider){if (provider NULL){fprintf(stderr, Trying to add a null provider.\n);return false;}if (!validateProvider(provider)){delete provider;return false;}// 临时放进 addRequests表addRequests[ provider-plumaGetType() ].push_back(provider);return true;
} 上面代码中那个 plumaGetType () 函数其实是 Provider 的私有成员一般人访问不了但 Host 是它的友元类所以可以访问。代码中会先校验待添加的 Provider 是否合格如果合格则以 plumaGetType () 返回值为 key 值并向临时映射表 addRequests 中添加该 Provider 指针。所谓合格是指这个 Provider 的类型是 Host 感兴趣的并且其版本号也是合适的。 值得注意的是待添加的 Provider*只是临时先放进一个 addRequests 映射表中。addRequests 映射表的定义如下 【pluma-1.1/include/pluma/Host.hpp】
typedef std::mapstd::string, std::listProvider* TempProvidersMap;
......
TempProvidersMap addRequests;
那么这个临时性的 addRequests 映射表的内容会怎样处理呢说起来也简单会被 “搬移” 进 Host 的 knownTypes 映射表中某个 ProviderInfo 的内部列表去。main () 在调用完 connect () 函数后调用的 confirmAddictions () 就是做这个事情的 【pluma-1.1/src/pluma/Host.cpp】
bool Host::confirmAddictions(){if (addRequests.empty()) return false;TempProvidersMap::iterator it;for( it addRequests.begin() ; it ! addRequests.end() ; it){std::listProvider* lst it-second;std::listProvider*::iterator providerIt;for (providerIt lst.begin() ; providerIt ! lst.end() ; providerIt){knownTypes[it-first].providers.push_back(*providerIt);}}TempProvidersMap().swap(addRequests); // 清空addRequests表return true;
} 我们画一张调用关系图看看 我们可以通过这张调用关系图回顾一下主要流程就是在加载插件动态库并执行动态库里的 connect () 函数。该函数会将动态库里可用的所有 Provider * 记入 Host 的 knownTypes 映射表中。同时动态库对应的 DLibrary 对象也会插入 Pluma 管理类内部的 libraries 映射表中。 为了巩固知识我们把前文的两张图再整合一下。 5. 使用插件 Provider
5.1 pluma.getProviders() 在 Providers 都添加进 Pluma 管理类后我们就可以在需要时获取 provider 了为此 Pluma 类提供了 getProviders () 函数 【pluma-1.1/src/pluma/PluginManager.cpp】
const std::listProvider** PluginManager::getProviders(const std::string type) const{return host.getProviders(type);
}
【pluma-1.1/src/pluma/Host.cpp】
const std::listProvider** Host::getProviders(const std::string type) const{ProvidersMap::const_iterator it knownTypes.find(type);if (it ! knownTypes.end())return it-second.providers;return NULL;
}
代码很简单就是帮使用者把感兴趣的某类插件 Provider 全部找出来。如果当初我们已经通过 acceptProviderType () 注册了对应的类型PLUMA_PROVIDER_TYPE那么至少可以拿到一个 list否则就只能拿到 NULL 了。如果我们可以拿到若干 Provider就可以调用其 create () 函数创建对应的插件对象了。 当工作做完后用户应该及时 delete 掉之前创建出的插件对象。在程序退出之前用户应该调用 pluma.unloadAll () 删除所有插件 Provider 及 DLibrary 对象。DLibrary 对象析构时会自动关闭已经打开的动态链接库。 【pluma-1.1/src/pluma/PluginManager.cpp】
void PluginManager::unloadAll(){host.clearProviders();LibMap::iterator it;for (it libraries.begin() ; it ! libraries.end() ; it){delete it-second; // delete掉DLibrary对象}libraries.clear();
}
void Host::clearProviders(){ProvidersMap::iterator it;for (it knownTypes.begin() ; it ! knownTypes.end() ; it){std::listProvider* providers it-second.providers;std::listProvider*::iterator provIt;for (provIt providers.begin() ; provIt ! providers.end() ; provIt){delete *provIt; // delete掉Provider对象}std::listProvider*().swap(providers);}
}
6. 结束 至此Pluma 架构的主体代码就分析完毕了希望对大家有所帮助。