POCO C++库学习和分析 — Foundation库SharedLibrary模块分析_napu_sh的博客-CSDN博客

POCO C++库学习和分析 — Foundation库SharedLibrary模块分析

        对于一个不熟悉的开源库和模块,我觉的最好的学习方法莫过于:

        1.  使用库,看库实现了什么功能和接口;

        2.  抛开库,想一想,自己如何实现。可以想出的出来是最好的,想不出其实也没什么关系,至少有了疑问。

        3.  看库的内层代码,学习和比较作者思路。

1.  SharedLibrary的功能和使用

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

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

1.1 导出函数

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

        对于库提供方而言:

1// TestLibrary.cpp2#include <iostream>3#if defined(_WIN32)4#define LIBRARY_API __declspec(dllexport)5#else6#define LIBRARY_API7#endif8extern "C" void LIBRARY_API hello();9void hello()10{11       std::cout << "Hello, world!" << std::endl;12}

        对于使用方而言:

1// LibraryLoaderTest.cpp2#include "Poco/SharedLibrary.h"3using Poco::SharedLibrary;4typedef void (*HelloFunc)(); // function pointer type5int main(int argc, char** argv)6{7        std::string path("TestLibrary");8        path.append(SharedLibrary::suffix()); // adds ".dll" or ".so"9        SharedLibrary library(path); // will also load the library10        HelloFunc func = (HelloFunc) library.getSymbol("hello");11        func();12        library.unload();13        return 0;14}

        

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

1.2  导出类

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

        对于库提供方:

.h文件

1// AbstractPlugin.h2//3// This is used both by the class library and by the application.4#ifndef AbstractPlugin_INCLUDED5#define AbstractPlugin_INCLUDED6class AbstractPlugin7{8public:9		AbstractPlugin();10		virtual ~AbstractPlugin();11		virtual std::string name() const = 0;12};13#endif // AbstractPlugin.h

.cpp文件

1// AbstractPlugin.cpp2//3// This is used both by the class library and by the application.4#include "AbstractPlugin.h"5AbstractPlugin::AbstractPlugin()6{}7AbstractPlugin::~AbstractPlugin()8{}9 10 11// PluginLibrary.cpp12#include "AbstractPlugin.h"13#include "Poco/ClassLibrary.h"14#include <iostream>15class PluginA: public AbstractPlugin16{17public:18		std::string name() const19		{20			return "PluginA";21		}22};23class PluginB: public AbstractPlugin24{25public:26		std::string name() const27		{28			return "PluginB";29		}30};31 32POCO_BEGIN_MANIFEST(AbstractPlugin)33POCO_EXPORT_CLASS(PluginA)34POCO_EXPORT_CLASS(PluginB)35POCO_END_MANIFEST36 37// optional set up and clean up functions38void pocoInitializeLibrary()39{40		std::cout << "PluginLibrary initializing" << std::endl;41}42void pocoUninitializeLibrary()43{44		std::cout << "PluginLibrary uninitializing" << std::endl;45}

        对于使用方来说:

1// main.cpp2#include "Poco/ClassLoader.h"3#include "Poco/Manifest.h"4#include "AbstractPlugin.h"5#include <iostream>6typedef Poco::ClassLoader<AbstractPlugin> PluginLoader;7typedef Poco::Manifest<AbstractPlugin> PluginManifest;8int main(int argc, char** argv)9{10		PluginLoader loader;11		std::string libName("PluginLibrary");12		libName += Poco::SharedLibrary::suffix(); // append .dll or .so13		loader.loadLibrary(libName);14		PluginLoader::Iterator it(loader.begin());15		PluginLoader::Iterator end(loader.end());16		for (; it != end; ++it)17		{18			std::cout << "lib path: " << it->first << std::endl;19			PluginManifest::Iterator itMan(it->second->begin());20			PluginManifest::Iterator endMan(it->second->end());21			for (; itMan != endMan; ++itMan)22			std::cout << itMan->name() << std::endl;23		}24		AbstractPlugin* pPluginA = loader.create("PluginA");25		AbstractPlugin* pPluginB = loader.create("PluginB");26		std::cout << pPluginA->name() << std::endl;27		std::cout << pPluginB->name() << std::endl;28		loader.classFor("PluginA").autoDelete(pPluginA);29		delete pPluginB;30		loader.unloadLibrary(libName);31		return 0;32}

        上述例子给出了一个接口类AbstractPlugin的使用。很简单。看了以后,自然会存在一些疑问:
        第一, 是否只能导出接口类,在上述例子中类PluginA和PluginB都从AbstractPlugin继承,所有函数都与AbstractPlugin完全一致。
        回答这个问题前,可以想一想,c++中的调用方使用被调者的条件(在这里是dll或者so的调用),调用方必须知道被调类的定义(.h文件中给出),即编译器必须知道被调类的内存布局。在上述例子中PluginA和PluginB定义并没有在头文件中给出,因此调用者只能调用AbstractPlugin基类定义的函数。事实上,SharedLibrary模块的框架只支持具有公共基类的类输出。如果调用者需要AbstractPlugin之外的接口,只能重新定义另一个接口类。
        对于使用和设计dll的人来说,导出的类千奇百怪,SharedLibrary模块只能导出基类的公共接口,有点不能接受。有一个变通的方法可以让使用dll的人绕过模块的限制,即dll导出时,导出的全部为工厂类,同时把真正需要的类的头文件抛出。

        第二,  如果一个dll或者so中存在多个接口类,是否支持输出。         支持

        第三, 导出的接口类存在于命名空间中,是否支持输出,如何使用。         支持

2  SharedLibrary模块设计分析

        上面我们已经看到了SharedLibrary的使用和功能。我们自己设计的话,跨平台,这个不是问题,只是一些系统API函数的使用。如何实时动态导出各种性质的类,并且不让使用者觉得过于繁琐呢?这个有点难。为了调用者方便,在上面的例子里,我们看到调用者是可以通过类名创建类的,这个看起来实在有点像java和c#里的反射。还是直接看POCO代码吧。先给出这个模块的类图。

图片[1]就去ID网POCO C++库学习和分析 — Foundation库SharedLibrary模块分析_napu_sh的博客-CSDN博客就去ID网97ID网

        还是倒过来看,SharedLibrary即为框架,就必须满足使用者的一般要求。

        1.    必须可以加载和卸载多个库多次

        2.    支持linux和window不同方式加载

        3.    能够加载和创建库内类

        为了实现对于上述要求,SharedLibrary抽象了结构体LibraryInfo,来抽象单个库的加载和卸载:

1struct LibraryInfo<Base>2{3	SharedLibrary* pLibrary;				// 加载库的方式4	const Manifest<Base> *   pManifest;		        // 单个库的class清单5	int            refCount;				// 库加载次数6};

        对于多个类ClassLoader实际上就是LibraryInfo的一个集合,这样就实现多个类加载。

1template <class Base> class ClassLoader2{3	std::map<std::string, LibraryInfo>  _map;4}

         库加载进去后,如何动态加载和创建类的类型呢。首先看一个库中可能存在多个类,必须对多个类进行管理。这个由Manifest实现。

1template <class B> class Manifest: public ManifestBase2{3	std::map<std::string, const Meta*> _metaMap;4}

         而对于每个类而言,如何实现动态创建呢。再来看由MetaObject类。

1template <class C, class B>2class MetaObject: public AbstractMetaObject<B>3	/// A MetaObject stores some information4	/// about a C++ class. The MetaObject class5	/// is used by the Manifest class.6	/// A MetaObject can also be used as an object7	/// factory for its class.8{9public:10	MetaObject(const char* name): AbstractMetaObject<B>(name)11	{12	}13 14	~MetaObject()15	{16	}17 18	B* create() const19	{20		return new C;21	}22	23	B& instance() const24	{25		throw InvalidAccessException("Not a singleton. Use create() to create instances of", this->name());26	}27	28	bool canCreate() const29	{30		return true;31	}

             OK。层级结构出来了,它的内部实现其实很简单。对于调用者,其实逻辑就更加简单了。首先调用者创建ClassLoader类,并调用loadLibrary(const std::string& path, const std::string& manifest)函数。

1void loadLibrary(const std::string& path, const std::string& manifest)2		/// Loads a library from the given path, using the given manifest. 3		/// Does nothing if the library is already loaded.4		/// Throws a LibraryLoadException if the library5		/// cannot be loaded or does not have a Manifest.6		/// If the library exports a function named "pocoInitializeLibrary",7		/// this function is executed.8		/// If called multiple times for the same library,9		/// the number of calls to unloadLibrary() must be the same10		/// for the library to become unloaded.11{12	FastMutex::ScopedLock lock(_mutex);13 14	typename LibraryMap::iterator it = _map.find(path);15	if (it == _map.end())16	{17		LibraryInfo li;18		li.pLibrary  = new SharedLibrary(path);19		li.pManifest = new Manif();20		li.refCount  = 1;21		try22		{23			std::string pocoBuildManifestSymbol("pocoBuildManifest");24			pocoBuildManifestSymbol.append(manifest);25			if (li.pLibrary->hasSymbol("pocoInitializeLibrary"))26			{27				InitializeLibraryFunc initializeLibrary = (InitializeLibraryFunc) li.pLibrary->getSymbol("pocoInitializeLibrary");28				initializeLibrary();29			}30			if (li.pLibrary->hasSymbol(pocoBuildManifestSymbol))31			{32				BuildManifestFunc buildManifest = (BuildManifestFunc) li.pLibrary->getSymbol(pocoBuildManifestSymbol);33				if (buildManifest(const_cast<Manif*>(li.pManifest)))34					_map[path] = li;35				else36					throw LibraryLoadException(std::string("Manifest class mismatch in ") + path, manifest);37			}38			else throw LibraryLoadException(std::string("No manifest in ") + path, manifest);39		}40		catch (...)41		{42			delete li.pLibrary;43			delete li.pManifest;44			throw;45		}46	}47	else48	{49		++it->second.refCount;50	}51}

          看一下过程:

          1. 加载库

          2. 库初始化,pocoInitializeLibrary

          3. BuildManifestFunc, 这是什么?关键之处,这是库内函数的自注册,是它实现了类的创建,这个由库实现者实现。为此POCO提供了一串宏来简化开发者的负担。这个和MFC中宏作用是非常相似,可以认为都是在编译时实现多态。

1#define POCO_BEGIN_MANIFEST_IMPL(fnName, base) 2	bool fnName(Poco::ManifestBase* pManifest_)										3	{																				4		typedef base _Base;															5		typedef Poco::Manifest<_Base> _Manifest;									6		std::string requiredType(typeid(_Manifest).name());							7		std::string actualType(pManifest_->className());							8		if (requiredType == actualType)												9		{																			10			Poco::Manifest<_Base>* pManifest = static_cast<_Manifest*>(pManifest_);11 12 13#define POCO_BEGIN_MANIFEST(base) 14	POCO_BEGIN_MANIFEST_IMPL(pocoBuildManifest, base)15 16 17#define POCO_BEGIN_NAMED_MANIFEST(name, base)	18	POCO_DECLARE_NAMED_MANIFEST(name)			19	POCO_BEGIN_MANIFEST_IMPL(POCO_JOIN(pocoBuildManifest, name), base)20 21 22#define POCO_END_MANIFEST 23			return true;	24		}					25		else return false;	26	}27 28 29#define POCO_EXPORT_CLASS(cls) 30    pManifest->insert(new Poco::MetaObject<cls, _Base>(#cls));

          至此,所以的环节都被串了起来。 泛型和宏的结合,实现的相当好。

         其他:

       1. 对于 AbstractMetaObject类除了动态创建类还有其他的一个功能,就是垃圾收集,不过这是个附属品。它通过接口autoDelete实现。

       图片[2]就去ID网POCO C++库学习和分析 — Foundation库SharedLibrary模块分析_napu_sh的博客-CSDN博客就去ID网97ID网

          2.  MetaSingleton类是个单件类,和MetaObject不能同时存在,这有库的提供者决定。

          3.  对操作系统实现的区别被封装在 SharedLibrary类中。类结构如下

图片[3]就去ID网POCO C++库学习和分析 — Foundation库SharedLibrary模块分析_napu_sh的博客-CSDN博客就去ID网97ID网

(版权所有,转载时请注明作者和出处 http://blog.csdn.net/arau_sh/article/details/8569638

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

昵称

取消
昵称表情代码图片

    请登录后查看评论内容