POCO库 Foundation::SharedLibrary模块分析

来源:互联网 发布:石家庄淘宝拍照 编辑:程序博客网 时间:2024/06/05 07:14
Foundation中的SharedLibrary实现跨平台的dll动态加载。
     具体使用方法和简介可见:ShareLibrary官方文档

SharedLibrary导出函数


SharedLibrary类的简单用法

    通过SharedLibrary可以实现函数导出和类导出,函数导出是最简单的,Dll提供方除了函数需要使用extern "C"声明之外,和普通C++编写的dll并无区别,在官方文档的例子中,dll使用方代码也很简单:
     
[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">// LibraryLoaderTest.cpp  
  2. #include "Poco/SharedLibrary.h"  
  3. using Poco::SharedLibrary;  
  4. typedef void (*HelloFunc)(); // function pointer type  
  5. int 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 library  
  10.         HelloFunc func = (HelloFunc) library.getSymbol("hello");  
  11.         func();  
  12.         library.unload();  
  13.         return 0;  
  14. }</span>  
    其中,SharedLibray::suffix()根据不同的平台和编译模式为dll选择不同的后缀,比如Windows Release版本,则直接添加".dll"后缀,而Windows Debug版本则添加"d.dll"后缀。ShareLibrary在构造时自动加载该动态链接库(在Windows下调用LoadLibrary),然后通过getSymbol(Windows下的GetProcAddress)获取函数地址,这也是之前说要在dll中在函数前用extern "C"声明的原因。

SharedLibrary类的跨平台实现

    SharedLibrary是如何实现跨平台的呢?在Foundation/Inlcude/poco/SharedLibrary.h中,可以看到SharedLibrary从一个叫SharedLibraryImpl的类私有继承,而在SharedLibrary中,几乎所有函数都调用对应的Impl函数:
源代码文件位置:Foundation/src/SharedLibrary.cpp
[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">SharedLibrary::SharedLibrary()  
  2. {  
  3. }  
  4.   
  5.   
  6. SharedLibrary::SharedLibrary(const std::string& path)  
  7. {  
  8.     loadImpl(path, 0);  
  9. }  
  10.   
  11.   
  12. SharedLibrary::SharedLibrary(const std::string& path, int flags)  
  13. {  
  14.     loadImpl(path, flags);  
  15. }  
  16.   
  17.   
  18. SharedLibrary::~SharedLibrary()  
  19. {  
  20. }  
  21.   
  22.   
  23. void SharedLibrary::load(const std::string& path)  
  24. {  
  25.     loadImpl(path, 0);  
  26. }  
  27.   
  28.   
  29. void SharedLibrary::load(const std::string& path, int flags)  
  30. {  
  31.     loadImpl(path, flags);  
  32. }  
  33.   
  34.   
  35. void SharedLibrary::unload()  
  36. {  
  37.     unloadImpl();  
  38. }  
  39.   
  40.   
  41. bool SharedLibrary::isLoaded() const  
  42. {  
  43.     return isLoadedImpl();  
  44. }  
  45.   
  46.   
  47. bool SharedLibrary::hasSymbol(const std::string& name)  
  48. {  
  49.     return findSymbolImpl(name) != 0;  
  50. }  
  51.   
  52.   
  53. void* SharedLibrary::getSymbol(const std::string& name)  
  54. {  
  55.     void* result = findSymbolImpl(name);  
  56.     if (result)  
  57.         return result;  
  58.     else  
  59.         throw NotFoundException(name);  
  60. }  
  61.   
  62.   
  63. const std::string& SharedLibrary::getPath() const  
  64. {  
  65.     return getPathImpl();  
  66. }  
  67.   
  68.   
  69. std::string SharedLibrary::suffix()  
  70. {  
  71.     return suffixImpl();  
  72. }</span>  

而这些函数,恰好在其父类SharedLibraryImpl中实现,而在SharedLibrary.h中,还有这一段:
[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#if defined(hpux) || defined(_hpux)  
  2. #include "SharedLibrary_HPUX.cpp"  
  3. #elif defined(POCO_VXWORKS)  
  4. #include "SharedLibrary_VX.cpp"  
  5. #elif defined(POCO_OS_FAMILY_UNIX)  
  6. #include "SharedLibrary_UNIX.cpp"  
  7. #elif defined(POCO_OS_FAMILY_WINDOWS) && defined(POCO_WIN32_UTF8)  
  8. #include "SharedLibrary_WIN32U.cpp"  
  9. #elif defined(POCO_OS_FAMILY_WINDOWS)  
  10. #include "SharedLibrary_WIN32.cpp"  
  11. #elif defined(POCO_OS_FAMILY_VMS)  
  12. #include "SharedLibrary_VMS.cpp"  
  13. #endif</span>  
     通过判断其平台,包含对应的ShareLibrary_*头文件,而在这些文件中,提供了各自的SharedLibraryImpl实现,也就是说,在子类SharedLibrary中调用的load,findSymbol,unload等函数,都最终调用其父类的函数。SharedLibrary_*实现了各个平台下的动态加载功能,而SharedLibrary则通过继承于它统一了这种接口,通过load,getSymbol等函数提供给使用方。
     打开Foundation/src/SharedLibrary_Win32.cpp文件,可以找到SharedLibrary在Windows下的实现,发现下面几个关键函数:
[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">void SharedLibraryImpl::loadImpl(const std::string& path, int /*flags*/)  
  2. {  
  3.     FastMutex::ScopedLock lock(_mutex);  
  4.   
  5.     if (_handle) throw LibraryAlreadyLoadedException(_path);  
  6.     DWORD flags(0);  
  7.     Path p(path);  
  8.     if (p.isAbsolute()) flags |= LOAD_WITH_ALTERED_SEARCH_PATH;  
  9.     _handle = LoadLibraryExA(path.c_str(), 0, flags);  
  10.     if (!_handle) throw LibraryLoadException(path);  
  11.     _path = path;  
  12. }  
  13. void* SharedLibraryImpl::findSymbolImpl(const std::string& name)  
  14. {  
  15.     FastMutex::ScopedLock lock(_mutex);  
  16.   
  17.     if (_handle)  
  18.     {  
  19.         return (void*) GetProcAddress((HMODULE) _handle, name.c_str());  
  20.     }  
  21.     else return 0;  
  22. }  
  23. std::string SharedLibraryImpl::suffixImpl()  
  24. {  
  25. #if defined(_DEBUG)  
  26.     return "d.dll";  
  27. #else  
  28.     return ".dll";  
  29. #endif  
  30. }</span>  


SharedLibrary导出类


    通过ClassLoader导出类的使用例子

SharedLibrary通过宏和泛型为我们提供了导出类的接口,文档中一个导出类的例子如下:
dll方头文件
[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">// AbstractPlugin.h  
  2. //  
  3. // This is used both by the class library and by the application.  
  4. #ifndef AbstractPlugin_INCLUDED  
  5. #define AbstractPlugin_INCLUDED  
  6. class AbstractPlugin  
  7. {  
  8. public:  
  9.         AbstractPlugin();  
  10.         virtual ~AbstractPlugin();  
  11.         virtual std::string name() const = 0;  
  12. };  
  13. #endif // AbstractPlugin.h</span>  
dll方cpp文件,包含一个基类实现文件AbstractPlugin.cpp和派生类实现文件PluginLibrary.cpp
[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">// AbstractPlugin.cpp  
  2. //  
  3. // This is used both by the class library and by the application.  
  4. #include "AbstractPlugin.h"  
  5. AbstractPlugin::AbstractPlugin()  
  6. {}  
  7. AbstractPlugin::~AbstractPlugin()  
  8. {}  
  9.   
  10.   
  11. // PluginLibrary.cpp  
  12. #include "AbstractPlugin.h"  
  13. #include "Poco/ClassLibrary.h"  
  14. #include <iostream>  
  15. class PluginA: public AbstractPlugin  
  16. {  
  17. public:  
  18.         std::string name() const  
  19.         {  
  20.             return "PluginA";  
  21.         }  
  22. };  
  23. class PluginB: public AbstractPlugin  
  24. {  
  25. public:  
  26.         std::string name() const  
  27.         {  
  28.             return "PluginB";  
  29.         }  
  30. };  
  31. //用POCO提供的宏来生成类清单  
  32. //这个宏展开实际是个函数声明,该函数由POCO在加载dll时自动调用,完成清单的加载  
  33. POCO_BEGIN_MANIFEST(AbstractPlugin)  
  34. POCO_EXPORT_CLASS(PluginA)  
  35. POCO_EXPORT_CLASS(PluginB)  
  36. POCO_END_MANIFEST  
  37.   
  38. // optional set up and clean up functions  
  39. void pocoInitializeLibrary()  
  40. {  
  41.         std::cout << "PluginLibrary initializing" << std::endl;  
  42. }  
  43. void pocoUninitializeLibrary()  
  44. {  
  45.         std::cout << "PluginLibrary uninitializing" << std::endl;  
  46. }</span>  

dll使用方:
[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">// main.cpp  
  2. #include "Poco/ClassLoader.h"  
  3. #include "Poco/Manifest.h"  
  4. #include "AbstractPlugin.h"  
  5. #include <iostream>  
  6. typedef Poco::ClassLoader<AbstractPlugin> PluginLoader;  
  7. typedef Poco::Manifest<AbstractPlugin> PluginManifest;  
  8. int main(int argc, char** argv)  
  9. {  
  10.         PluginLoader loader;  
  11.         std::string libName("PluginLibrary");  
  12.         libName += Poco::SharedLibrary::suffix(); // append .dll or .so  
  13.         loader.loadLibrary(libName);  
  14.         PluginLoader::Iterator it(loader.begin());  
  15.         PluginLoader::Iterator end(loader.end());  
  16.         for (; it != end; ++it)//遍历该loader加载的所有dll 这里只有一个  
  17.         {  
  18.             std::cout << "lib path: " << it->first << std::endl;//输出该dll路径  
  19.             PluginManifest::Iterator itMan(it->second->begin());  
  20.             PluginManifest::Iterator endMan(it->second->end());  
  21.             for (; itMan != endMan; ++itMan)//遍历该dll的类清单  
  22.             std::cout << itMan->name() << std::endl;//输出类名  
  23.         }  
  24.         AbstractPlugin* pPluginA = loader.create("PluginA");//创建PluginA类对象  
  25.         AbstractPlugin* pPluginB = loader.create("PluginB");  
  26.         std::cout << pPluginA->name() << std::endl;//输出PluginA类名  
  27.         std::cout << pPluginB->name() << std::endl;  
  28.         loader.classFor("PluginA").autoDelete(pPluginA);//将pPluginA所指对象所有权交给loader 当loader析构时会自动释放  
  29.         delete pPluginB;  
  30.         loader.unloadLibrary(libName);  
  31.         return 0;  
  32. }</span>  
导出类明显要复杂得多,同时也可以看出SharedLibrary导出类的一些限制:只能导出dll中的具有公共接口类,但是一个dll中可以支持导出多个接口类。

    ClassLoader层次结构分析

我们从上往下看。
首先是ClassLoader:
    
[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">typedef Poco::ClassLoader<AbstractPlugin> PluginLoader;</span>  

  它实现将dll导入,读取dll中提供的导出类清单,并且可以根据类名创建其对象。它的核心就是一个LibraryInfo的集合:
[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">typedef std::map<std::string, LibraryInfo> LibraryMap;  
  2. </span>  

  而LibraryInfo包含了一个dll被加载的基本信息:
[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">typedef Manifest<Base> Manif;  
  2. struct LibraryInfo  
  3.     {  
  4.         SharedLibrary* pLibrary; //负责该dll的加载和卸载  
  5.         const Manif*   pManifest;//该dll导出类的清单  
  6.         int            refCount; //该dll被加载的次数  
  7.     };</span>  

  这里面最重要的是Manifest<Base>,它包含了该dll所有导出类的信息。它负责维护该dll中导出类的类名和类信息的映射:
[cpp] view plaincopyprint?
  1. <pre name="code" class="cpp"><span style="font-size:18px;">typedef AbstractMetaObject<B> Meta;  
  2. typedef std::map<std::string, const Meta*> MetaMap;</span></pre>  
  3. <pre></pre>  
  4. <pre></pre>  
  5. <pre></pre>  
  6. <pre></pre>  
  7. <pre></pre>  
  8. <pre></pre>  
  9. <pre></pre>  
  10. <pre></pre>  
  11. <pre></pre>  
  12. <pre></pre>  
  Meta是一个抽象类,一个Meta负责维护一个导出类,它提供最重要的接口,包括获取类名,创建该类对象,维护该类指针(即autoDelete,当Meta对象析构时自动释放该指针)等。它有两个派生类:
[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">//负责维护普通类  
  2. template <class C, class B>  
  3. class MetaObject: public AbstractMetaObject<B>  
  4. //负责维护单例模式类  
  5. template <class C, class B>   
  6. class MetaSingleton: public AbstractMetaObject<B> </span>  
注意,这两个派生类的模板参数不再是一个,而是两个,class B是dll中的公共接口类,class C就是真正维护的类。
看一下AbstractMetaObject的源码:
源代码文件位置:Foundation/Include/poco/MetaObject.h
[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">template <class B>  
  2. class AbstractMetaObject  
  3. {  
  4. public:  
  5.     AbstractMetaObject(const char* name): _name(name)  
  6.     {  
  7.     }  
  8. <span style="white-space:pre">  </span>//在析构时,释放被委托(通过autoDelete)的该类指针  
  9.     virtual ~AbstractMetaObject()  
  10.     {  
  11.         for (typename ObjectSet::iterator it = _deleteSet.begin(); it != _deleteSet.end(); ++it)  
  12.         {  
  13.             delete *it;  
  14.         }  
  15.     }  
  16. <span style="white-space:pre">  </span>//获取该类名字,_name从构造函数中传入  
  17.     const char* name() const  
  18.     {  
  19.         return _name;  
  20.     }  
  21.   
  22.     virtual B* create() const = 0;//创建接口 由普通类实现  
  23.           
  24.     virtual B& instance() const = 0;//实例接口 由单例类实现  
  25.   
  26.     virtual bool canCreate() const = 0;//是否是普通类  
  27.   
  28.     virtual void destroy(B* pObject) const//释放指针(前提是该对象有该指针的所有权)  
  29.     {  
  30.         typename ObjectSet::iterator it = _deleteSet.find(pObject);//在托管指针集合中寻找该指针  
  31.           
  32.         if (it != _deleteSet.end())//如果找到 则说明该对象有该指针所有权 才释放  
  33.         {  
  34.             _deleteSet.erase(pObject);  
  35.             delete pObject;  
  36.         }  
  37.     }  
  38.   
  39.     B* autoDelete(B* pObject) const//被托管的指针都放在_deleteSet集合中  
  40.     {  
  41.         if (this->canCreate()) // guard against singleton  
  42.         {  
  43.             poco_check_ptr (pObject);  
  44.             _deleteSet.insert(pObject);  
  45.         }  
  46.         else throw InvalidAccessException("Cannot take ownership of"this->name());  
  47.   
  48.         return pObject;  
  49.     }  
  50.   
  51.     virtual bool isAutoDelete(B* pObject) const  
  52.     {  
  53.         return _deleteSet.find(pObject) != _deleteSet.end();  
  54.     }  
  55.   
  56. private:  
  57.     AbstractMetaObject();  
  58.     AbstractMetaObject(const AbstractMetaObject&);  
  59.     AbstractMetaObject& operator = (const AbstractMetaObject&);  
  60.   
  61.     typedef std::set<B*> ObjectSet;  
  62.       
  63.     const char* _name;<span style="white-space:pre">    </span>//维护的类名  
  64.     mutable ObjectSet _deleteSet;//托管的指针集合  
  65. };</span>  
    可以看出,该类完成了除了创建对象(对于单例类,则是获取实例)之外的所有功能,包括获取所维护的类的名字,托管该类指针和释放该类指针等。这里需要先记住,类名是通过构造函数传入的。
    对于MetaObject类就比较简单了,基本只负责了类的创建:
源代码文件位置:Foundation/Include/poco/MetaObject.h
[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">template <class C, class B>  
  2. class MetaObject: public AbstractMetaObject<B>  
  3. {  
  4. public:  
  5.     MetaObject(const char* name): AbstractMetaObject<B>(name)  
  6.     {  
  7.     }  
  8.   
  9.     ~MetaObject()  
  10.     {  
  11.     }  
  12.   
  13.     B* create() const  
  14.     {  
  15.         return new C;  
  16.     }  
  17.       
  18.     B& instance() const  
  19.     {  
  20.         throw InvalidAccessException("Not a singleton. Use create() to create instances of"this->name());  
  21.     }  
  22.       
  23.     bool canCreate() const  
  24.     {  
  25.         return true;  
  26.     }  
  27. };</span>  
    同样,这里还需记住,类的创建是通过MetaObject的模板参数实现的。
    那么,到现在,整个层次结构已经比较清楚了,并且明白了类是如何被维护的,类名获取和类对象创建是在哪里完成的。现在还剩下的问题是,类名和类模板参数是怎么被放进去的,并且它们是怎么被联系在一起的(通过类名创建对象)。
整个ClassLoader的结构类图:


    ClassLoader流程分析

    在清楚了结构之后,在按照调用顺序跟踪一边,看这些结构都是如何被建立和组织的。
    在我们前面的例子中,这些结构的建立和组织都发生在一个函数:loadLibrary中:
源代码文件位置:Foundation/Include/poco/ClassLoader.h
[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">void loadLibrary(const std::string& path, const std::string& manifest)  
  2.     {  
  3.         FastMutex::ScopedLock lock(_mutex);  
  4.   
  5.         typename LibraryMap::iterator it = _map.find(path);//在维护的map<std::string, LibraryInfo>中寻找该dll  
  6.         if (it == _map.end())//如果该dll当前没有被该ClassLoader加载  
  7.         {  
  8.             LibraryInfo li;  
  9.             li.pLibrary  = new SharedLibrary(path);//在SharedLibrary类的构造函数中,完成dll加载  
  10.             li.pManifest = new Manif();//typedef Manifest<Base> Manif; 构建一个新的空清单  
  11.             li.refCount  = 1;  
  12.             try  
  13.             {  
  14.                 std::string pocoBuildManifestSymbol("pocoBuildManifest");  
  15.                 pocoBuildManifestSymbol.append(manifest);//对于我们前面但参数的调用,manifest为""  
  16.                 if (li.pLibrary->hasSymbol("pocoInitializeLibrary"))//如果有自定义的初始化函数 执行  
  17.                 {  
  18.                     InitializeLibraryFunc initializeLibrary = (InitializeLibraryFunc) li.pLibrary->getSymbol("pocoInitializeLibrary");  
  19.                     initializeLibrary();  
  20.                 }  
  21.                 if (li.pLibrary->hasSymbol(pocoBuildManifestSymbol))//如果找到构造清单函数 执行  
  22.                 {  
  23.                     BuildManifestFunc buildManifest = (BuildManifestFunc) li.pLibrary->getSymbol(pocoBuildManifestSymbol);  
  24.                     if (buildManifest(const_cast<Manif*>(li.pManifest)))//将空清单的指针传入函数,pocoBuildManifest函数,这一步就是构造函数导出清单的关键  
  25.                         _map[path] = li;//如果构建清单成功 则加入到map<string, LibraryInfo>  
  26.                     else  
  27.                         throw LibraryLoadException(std::string("Manifest class mismatch in ") + path, manifest);  
  28.                 }  
  29.                 else throw LibraryLoadException(std::string("No manifest in ") + path, manifest);  
  30.             }  
  31.             catch (...)  
  32.             {  
  33.                 delete li.pLibrary;  
  34.                 delete li.pManifest;  
  35.                 throw;  
  36.             }  
  37.         }  
  38.         else//如果该dll当前已经被该ClassLoader加载 则添加引用计数  
  39.         {  
  40.             ++it->second.refCount;  
  41.         }  
  42.     }  
  43. </span>  
    在我们前面的例子中,我们自定义了pocoInitializeLibrary函数,因此它会在该dll刚被加载后就执行,而后面看到的pocoBuildManifest函数好像我们并没有定义,但是前面说过供ClassLoader使用的每个dll都必须有一个导出类清单,实际上,为了最大程度简化使用者的负担,POCO将pocoBuildManifest函数封装成宏了,这就是我们前面在dll提供方cpp文件中看到的:
[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">POCO_BEGIN_MANIFEST(AbstractPlugin)  
  2. POCO_EXPORT_CLASS(PluginA)  
  3. POCO_EXPORT_CLASS(PluginB)  
  4. POCO_END_MANIFEST</span>  
在Foundation/Include/poco/ClassLibrary.h中找到该宏的定义:
[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">#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));  
  31.   
  32.   
  33. #define POCO_EXPORT_SINGLETON(cls) \  
  34.     pManifest->insert(new Poco::MetaSingleton<cls, _Base>(#cls));</span>  
    现在一切都明了了,这个宏展开后,就变成了pocoBuildManifest函数,并且在POCO_EXPORT_CLASS中传入了派生类和接口类作为模板参数,传入#cls也就是类名给MetaObject构造函数参数,MetaObject在构造时会将类名传给其基类AbstractMetaObject,这样其基类就可以实现name()接口了。(#可以使其后面的cls替换为字面量,比如#define TOSTRING(a) #a 那么TOSTRING(PluginA)将被替换为"PluginA")。
   顺便提一下,上面的POCO_BEGIN_NAMED_MANIFEST(name,base)宏实现自命名的清单导出函数名,这样我们就可以在dll中定义多份导出清单,然后在ClassLoader的loadLibrary函数调用中,将第二参数指定为我们想要只用的那份清单名(当第二参数未提供时,默认清单名即为pocoBuildManifest),这样就实现了一个dll的多个接口类导出。
   为了查看方便,我将宏展开:
[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">bool pocoBuildManifest(Poco::ManifestBase* pManifest_)                                          
  2. {                                                                                 
  3.     typedef base _Base;                                                           
  4.     typedef Poco::Manifest<_Base> _Manifest;  
  5.       
  6.     //提供方的类清单Manifest类名 由宏POCO_BEGIN_MANIFEST提供的接口类作为模板参数 在这里是requiredType="class Manifest<AbstractPlugin>"     
  7.     std::string requiredType(typeid(_Manifest).name());  
  8.     //使用方的类清单Manifest类名 这是由使用方初始化ClassLoader提供模板参数时确认的 在例子中我们使用的ClassLoader<AbstractPlugin> 这个模板参数提供给Manifest后 得到的actualType="class Manifest<AbstractPlugin>"                             
  9.     std::string actualType(pManifest_->className());               
  10.     if (requiredType == actualType)                     //如果匹配 则将导出类加到类清单                         
  11.     {                                                                             
  12.         Poco::Manifest<_Base>* pManifest = static_cast<_Manifest*>(pManifest_);  
  13.         pManifest->insert(new Poco::MetaObject<PluginA, AbstractPlugin>("PluginA"));  
  14.         pManifest->insert(new Poco::MetaObject<PluginB, AbstractPlugin>("PluginB"));  
  15.         return true;      
  16.     }                     
  17.     else return false;    
  18. }</span>  

   现在整个结构和组织都理清楚了,现在再来看看ClassLoader的create就比较简单了,什么信息都有了,它只是组织一下其组件,通过类名在类清单Manifest中找到其对应的MetaObject类,然后再调用MetaObject类的create(),Over。
源代码文件位置:Foundation/Include/poco/ClassLoader.h
[cpp] view plaincopyprint?
  1. <span style="font-size:18px;">const Meta* findClass(const std::string& className) const  
  2. {  
  3.     FastMutex::ScopedLock lock(_mutex);  
  4.   
  5.     for (typename LibraryMap::const_iterator it = _map.begin(); it != _map.end(); ++it)  
  6.     {  
  7.         const Manif* pManif = it->second.pManifest;  
  8.         typename Manif::Iterator itm = pManif->find(className);  
  9.         if (itm != pManif->end())  
  10.             return *itm;  
  11.     }  
  12.     return 0;  
  13. }  
  14.   
  15. const Meta& classFor(const std::string& className) const  
  16. {  
  17.     const Meta* pMeta = findClass(className);  
  18.     if (pMeta)  
  19.         return *pMeta;  
  20.     else  
  21.         throw NotFoundException(className);  
  22. }  
  23.   
  24. Base* create(const std::string& className) const  
  25. {  
  26.     return classFor(className).create();  
  27. }</span>  
注:一个ClassLoader只能加载dll的一份类清单

总结:

    最后梳理一下整个过程:
    dll提供方:通过使用POCO_BEGIN_MANIFEST,POCO_EXPORT_CLASS等一系列宏来建立dll的pocoBuildManifest函数(可自定义命名),该函数以一个Manifest指针为参数,完成对导出类清单Manifest的填充。
    dll加载方:ClassLoader在loadLibrary函数中找到并调用pocoBuildManifest函数,传入new Manifest返回的空清单指针指针,该函数完成:
    1.为导出的各个类都建立一个对于的MetaObject(MetaSingleton)对象
2.将类名和对应类的MetaObject(MetaSingleton)对象放入类导出清单的map<string, Meta*>MetaMap中
    整个交互都建立在一个公共接口类的前提下,通过该公共接口类Base,建立了AbstractMetaObject<Base>模板类,该类实现对各个导出的派生类的统一,并且将所有导出类都放在map<string, AbstractMetaObject<Base>> MetaMap这个容器中(由Manifest类管理),这个容器将类名和类相关操作关联起来,最后通过指定类名来找到该类对应的MetaObect(或单例MetaSingleton)类,完成该类的管理,再由ClassLoader提供出来。

    整个过程就用户看来:
在提供dll时,使用两三个宏(并包含头文件ClassLibrary.h),指出要导出的类(需要有公共接口类)。
在使用dll时,使用ClassLoader类就可完成所有操作(加载库,创建类对象等)。

    ClassLoader将泛型和宏结合起来,并且通过容器实现了"拟多态"(通过公共接口统一导出,然后再用类名来找到具体派生类)。
    还有一点值得一提的就是ClassLoader Manifest都通过迭代器模式来使我们在遍历和使用整个结构的时候更方便,比如ClassLoader<B>::Iterator it; 对it->second返回的并不是LibraryInfo,而是Manifest*。而对Manifest::Iterator it进行解引用,得到的不是Pair<string, Meta*> 而直接是Meta*。从上面的例子的遍历过程也可以看出,这屏蔽了底层复杂的结构,极大的方便了使用。

    使用ClassLoader注意事项:
    1.ClassLoader只能导出dll中具有公共接口类的类
    2.dll使用方只能使用dll中公共接口类提供的接口
    3.公共接口类一般为抽象类,如果不为抽象类,那么其所有函数也应该是虚函数。如果接口类实现了一个普通函数,那么使用方通过ClassLoader将无法调用该方法,会出现链接错误(本人测试出来的,但是还不清楚为什么)。比如,如果在AbstractPlugin中加入一个函数:
void SayGoodBye()
{
    std::cout<<"GoodBye!"<<std::end;
}
     那么通过ClassLoader create("PluginA")返回的AbstractPlugin指针调用SayGoodBye() VS2008将会报错:
error LNK2001: 无法解析的外部符号 "public: void __thiscall AbstractPlugin::SayGoodBye(void)" (?SayGoodBye@AbstractPlugin@@QAEXXZ)
     但是将该函数声明为virtual就正常了。
    4.一个ClassLoader只能从一个dll中导出一份清单
    5.一个dll可以有多个导出类清单(通过POCO_BEGIN_NAMED_MANIFEST重命名pocoBuildManifest函数)
    6.还有一点我也比较奇怪的是,ClassLoader维护的map<string, LibraryInfo> LibraryMap是一个映射,并且提供了迭代器来遍历,应该是可以加载多个dll的。但是ClassLoader又需要一个接口类作为模板参数实例化,难道它只能加载多个具有共同接口类的dll吗?

  转载请注明出处:http://blog.csdn.net/wudaijun/article/details/9374233

0 0