引言
在做项目过程中库函数是一个很重要的模块,而我们往往将每个功能独立成一个函数,这样能够提高函数的复用性。
根据使用过的场景,项目过程中对库函数的引用总共3种方式:
| 方式 | 链接时刻 | 库指定时刻 | 特点 | 
| 静态链接 | 编译时 | 编译时 | 编译时检查链接错误,编入运行程序(运行程序独立) | 
| 动态链接 | 运行时 | 编译时 | 编译时检查链接错误,运行时调入依赖库 | 
| 动态链接 | 运行时 | 运行时 | 编译时不做任何检查,运行时调入依赖库 | 
其中前两种是我们熟悉的,最后一种是“完全”动态方式,包括库文件的指定都是由代码完成的。
这里先重点讲解最后一种,后续会在补充,如果忘记了,可以留言催一下。
链接静态库
静态链接动态库
动态链接动态库
定义了一组接口函数,但无法得知具体的实现细节,可能是由别人实现,程序在运行时根据某个传入的参考,动态调用这一组接口,并按照特定的方式运行,在C++中多少像虚函数的特点-提供接口而不在乎具体的实现。
这种场景比较适合于一个框架(开发者A),多个不同的细节实现(开发者B,C…)的一种应用。
从上面的描述,要完成本项功能,包括调用框架的编写(相当于服务器端)和被调用库函数的编写(相当于“客户端”)。而要做到跨平台,这两者必须都是跨平台的。
动态库编写
C语言有不带类的C和带类的C(C++),C++在编译时,实际上是要被转换为C的,所以从调用的角度来讲,只有函数,没有类。这样问题就来了:类如何导出?即调用者如何得到类的信息?
对于后出现的语言,例如Java,C#,这根本不是问题,语言的开发者已为我们考虑了这个情况。在微软的世界里(Windows),这也不是大问题,微软自己有一套规则。微软的COM的一个特点就是用来描述导出类的,但可惜并没有被大家接受。在Linux世界里,似乎只有一个个的函数能被“开放出来”,供其他开发者所调用。
如果要开发跨平台的库(不提供源代码),还是老老实实回归C语言吧。
Windows
Linux
Linux下实现起来比较简单,只在.h声明导出函数时,采用extern C包裹它们,如上面的例子所示,即是跨平台的解决方案,推荐使用。
如果函数不多,建议编写一个 .def文件方便一些,如果函数较多,且其声明不断变化,采用宏定义 __declspec(dllexport)较好,这样维护方便。
WINDOWS函数导出
具体实现
通过调用不同平台的接口,实现统一动态库加载的接口。主要实现了四个接口:加载动态库 LoadLib ,获取动态库函数 GetLibFun,释放动态库 FreeLib,获取错误码 FreeLib。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 
 | #if defined(_WIN32)
 #include <windows.h>
 typedef HMODULE  MODULE_HANDLE;
 #endif
 
 #if defined(__linux__)
 #include <dlfcn.h>
 typedef void *  MODULE_HANDLE;
 #endif
 
 MODULE_HANDLE LoadLib(const char *plname)
 {
 #if defined(_WIN32)
 return LoadLibraryA (plname);
 #elif defined(__linux__)
 return dlopen( plname, RTLD_NOW|RTLD_GLOBAL);
 #endif
 }
 void FreeLib(MODULE_HANDLE h)
 {
 if(h){
 #if defined(_WIN32)
 FreeLibrary(h);
 #elif defined(__linux__)
 dlclose (h);
 #endif
 }
 }
 void *GetLibFun(MODULE_HANDLE h, const char *pfname)
 {
 if(h){
 #if defined(_WIN32)
 return (void *)GetProcAddress(h, pfname);
 #elif defined(__linux__)
 return dlsym(h,pfname);
 #endif
 }
 return NULL;
 }
 const char  GetLastErr()
 {
 char p[100];
 int size=99;
 #if defined(_WIN32)
 sprintf(p,size, "%u",::GetLastError());
 #elif defined(__linux__)
 sprintf(p,size, "%s",dlerror());
 #endif
 return p;
 }
 
 class CDynamicLibrary
 {
 public:
 CDynamicLibrary()
 {
 m_hModule = NULL;
 }
 ~CDynamicLibrary()
 {
 FreeLib(m_hModule);
 }
 inline bool LoadLib(const char *lpname)
 {
 m_strDllName = lpname;
 m_hModule = LoadLib(lpname);
 return m_hModule!=NULL;
 }
 inline void *GetLibFun(const char *pfname)
 {
 return GetLibFun(m_hModule, pfname);
 }
 inline const char *GetLastErr()
 {
 char* msg = GetLastErr();
 m_strMsg = msg;
 return m_strMsg.c_str();
 }
 
 private:
 std::string m_strMsg;
 std::string m_strDllName;
 MODULE_HANDLE m_hModule;
 };
 
 | 
调用实例
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 
 | void testdll(){
 typedef bool (*F_DllInit)();
 typedef bool (*F_DllFree)();
 typedef const char* (*F_DllFunc)(const char *name);
 
 #if defined(_WIN32_PLATFROM_)
 char *pdllname = "dlldemo.dll";
 #endif
 #if defined(_LINUX_PLATFROM_)
 char *pdllname = "libdlldemo.so";
 #endif
 MODULE_HANDLE h = LoadLib(pdllname);
 if(h){
 F_DllInit dll_init = (F_DllInit)h.GetProc("DllInit");
 if(!dll_init){
 printf("\nFunction DllInit Error: %s",h.GetLastError());
 }else{
 bool init_flag = dll_init();
 }
 F_DllFunc dll_func = (F_DllFunc)h.GetProc("DllFunc");
 if(!dll_func){
 printf("\nFunction DllFunc Error: %s",h.GetLastError());
 }else{
 const char* name="123";
 const char* func_data = dll_func(name);
 }
 F_DllFree dll_free = (F_DllFree)h.GetProc("DllFree");
 if(!dll_free){
 printf("\nFunction DllFree Error: %s",h.GetLastError());
 }else{
 bool free_flag = dll_free();
 }
 
 }
 }
 
 | 
参考
- https://blog.csdn.net/guxch/article/details/7915404