引言
在做项目过程中库函数是一个很重要的模块,而我们往往将每个功能独立成一个函数,这样能够提高函数的复用性。
根据使用过的场景,项目过程中对库函数的引用总共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
。
1 2 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; };
|
调用实例
1 2 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