标签:
项目写了个logger,本来是代码的,大家单独包含都可以使用,但是后来项目整合,每个人的部分打成lib,而前端将logger打包一起编译成lib,后台按道理应该是不用包含 .cpp 文件也可以用到logger,因为如果后台也包含并编译,应该会和前端的编译单元中的 logger 重定义。
但是奇怪的是,非但不重定义,反而这边获取不到 io 流 (流是静态变量),匪夷所思,最后是采用命名空间解决了。
虽然那个问题没有完美解决,但是我决定好好搞一下 lib 和 dll ,因为仅就 logger 来说:
好处1 这个项目中,日志模块是完全独立的模块,从设计的角度来看,本来就应该独立开来打包,然后大家就可以一起用这个包,实现了松耦合;
好处2 logger 打成 lib 或者 dll,大家就可以像使用一些api一样松松使用,不仅用于这个项目,以后实验室的项目都可以使用。
另外,也可以彻底搞一下动态库和静态库。
最简单的定义,也是最具有可信信的:
一个 lib 文件是obj文件的集合。
当然,其中还夹杂着其他一些辅助信息,目的是为了让编译器能够准确找到对应的 obj 文件。我们可以通过tlib.exe(在tc2.0下的根目录)来对lib文件进行操作
dll 是运行时装载的函数实现代码,个人认为是部分代码段汇编对应的二进制码。
注: 本人对于静态编译和动态编译的概念几经查证,尚无定论,先留白在此,而后查证后更新。
如下解释只是个人理解。
动态编译: 可执行文件需要附带一个的动态链接库,动态链接库在运行时加载
静态编译: 将附加的静态链接库链接进最后的可执行文件,是编译期所做的事情
本人编译环境为: win7 vs2013,静态链接库为lib
我希望将 logger 打成一个 lib,供外部链接调用。理清包含关系如下:
(1) logger.h 包含 mutex.h 和一些其他头文件
(2) mutex.h 包含 pthread.h,并在代码中加入#pragma comment(lib, "pthreadVC2.lib")
,用到第三方的函数
即: logger.h —> mutex.h —> pthread.h
而 pthread 提供 pthread.h + pthread 相关 lib + pthread 相关 dll
正片开始:
我发现,如果选择生成 lib,编译选项里面并不能添加 “附加依赖项”,所以我大胆猜测 代码中编译指导链接 pthread lib 那句话在生成 lib 时没有用。
注释编译指导语句,生成 lib 成功~~
这也就是说,生成 lib 的时候,并不会进行链接,自然就不会进行重定义和外部定义的检查,只会进行将当前项目的编译单元生成中间文件 (这里是 .obj 文件) 打包,即使在代码中指明需要链接外部 lib。
即 lib 不能链接外部 lib~~
个人认为编译器这么做的目的,是为了防止很多 lib 重复定义同一个外部 lib。而且生成 lib 的确不能做链接,因为 lib 的最终职能是为了让可执行程序跑起来,生成 lib 就链接是没有意义的。
需包含上一步生成的 lib,使用#pragma comment(lib, "logger.lib")
完成
#include "logger.h"
#pragma comment(lib, "logger.lib")
int main(int argc, char *argv[])
{
initLogger("whInfolog.txt", "whWarnlog.txt", "whErrolog.txt", false);//初始化日志文件
LOG2(_INFO,"first log");
return 0;
}
编译器配置如下:
特别需要注意的是:
因为上一步生成 lib 时,没有包含 pthread 的 lib 文件,在最后生成可执行文件的时候,必须:
(1) 包含 lib 所在文件夹为库文件夹
(2) 在包含的用到 pthread lib 文件的头文件处 (这里是 mutex.h),使用#pragma comment(lib, "pthreadVC2.lib")
或项目属性中设置附加依赖项
(3) pthread dll 放到可执行程序目录,或者放到环境变量中
然后就可以正常使用了。
因为是 win 平台,所以生成动态库是 dll
dll 是实现的二进制码,运行时装载,自然在项目属性中有了 “附加依赖项” 的选项,这意味着生成 dll 时,是需要链接的,dll 中包含外部 lib 的实现。
于是这里需要包含,pthread lib 目录,并使用#pragma comment(lib, "pthreadVC2.lib")
或项目属性中设置附加依赖项
另外由于 dll 的特殊性,dll 需要暴露在黑盒外的函数接口有特殊写法,详见代码:
/*
*
* function: 日志 dll API loggerAPI.h
*
* Date:2016-04-28
*
* Author: Bill Wang
*/
#ifndef _LOGGER_API_H_
#define _LOGGER_API_H_
#include "logger.h"
#define WH_DLL_EXPORTS
#ifdef WH_DLL_EXPORTS
#define WH_DLL_API __declspec(dllexport) //导出
#else
#define WH_DLL_API __declspec(dllimport) //导入
#endif
extern "C"
{
WH_DLL_API void api_InitLogger(const std::string& info_log_filename,
const std::string& warn_log_filename,
const std::string& error_log_filename,
bool isAppend = true);
WH_DLL_API std::ostream& api_WriteLogger(log_rank_t rank, const std::string reason);
WH_DLL_API void echoTest(); //echo测试
}
#endif
/*
*
* function: 日志 dll API loggerAPI.cpp
*
* Date:2016-04-28
*
* Author: Bill Wang
*/
#include "loggerAPI.h"
#include "logger.h"
#include <iostream>
using namespace std;
void api_InitLogger(const std::string& info_log_filename,
const std::string& warn_log_filename,
const std::string& error_log_filename,
bool isAppend) {
initLogger(info_log_filename, warn_log_filename, error_log_filename, isAppend);//初始化日志文件
}
std::ostream& api_WriteLogger(log_rank_t rank, const std::string reason) {
return LOG2(rank, reason);
}
//echo测试
void echoTest() {
cout << "Echo Success!!" << endl;
}
用 depends.exe 查看结果如下,可以清晰的看到,dll 是给出暴露接口的函数入口地址
//#include "loggerAPI.h"
#include<Windows.h>
#include<iostream>
#include<string>
using namespace std;
typedef enum log_rank {
_INFO,
_WARNING,
_ERROR,
_FATAL
}log_rank_t;
typedef void (*API_INITLOGGER)(const std::string& info_log_filename,
const std::string& warn_log_filename,
const std::string& error_log_filename,
bool isAppend);
typedef std::ostream& (*API_WRITELOGGER)(log_rank_t rank, const std::string reason);
typedef void (*API_ECHOTEST)();
int main(int argc, char *argv[])
{
HMODULE hm = ::LoadLibrary(TEXT("logger.dll"));
//如果Dll加载失败,释放它占用的资源
if (hm == NULL) {
cout << "hm is null" << endl;
::FreeLibrary(hm);
return 0;
}
API_ECHOTEST main_EchoTest = (API_ECHOTEST)::GetProcAddress(hm, "echoTest");
if (main_EchoTest == NULL) {
cout << "main_EchoTest is null" << endl;
::FreeLibrary(hm);
}
main_EchoTest();
API_INITLOGGER main_API_InitLogger = (API_INITLOGGER)::GetProcAddress(hm, "api_InitLogger");
main_API_InitLogger("whInfolog.txt", "whWarnlog.txt", "whErrolog.txt", false);//初始化日志文件
API_WRITELOGGER main_API_WriteLogger = (API_WRITELOGGER)::GetProcAddress(hm, "api_WriteLogger");
main_API_WriteLogger(_INFO, "first log");
::FreeLibrary(hm);
return 0;
}
如果细心可以发现,我上面代码中,将 #include "loggerAPI.h"
注释掉了,而改成了包含很多其他的头文件和枚举量申明,原因就是,使用 dll 其实没有必要包含实现 dll 的全部头文件,只要函数指针类型正确,所有变量类型无误,即可获得函数地址,读者可自行试验。
dll 的两边只要函数签名完全一致,即可
所以这边既不用包含 pthread.h,也不用包含 pthread lib库,不过 pthread dll 还是需要的,因为 logger.dll 中链接的 pthread lib 库,依赖 pthread dll
今天在看一篇博文的时候,发现一篇博文不少问题,关键是这篇文章还高居搜索榜不下。。。
http://blog.csdn.net/wuan584974722/article/details/7953213
文章反复提到 “动态lib相当于一个h文件,是对实现部分(.dll文件)的导出部分的声明”
我们都知道 dll 要用,必须要被解析,这个解析的过程是需要在函数内部实现的,也就是说,lib 在这里充当了一个中转站。比如在 lib 中定义一个函数 void lib_resolveEchoTest()
,这个函数专门解析 dll 中的 void EchoTest()
,然后就可以调用 lib 中的 void lib_resolveEchoTest()
完成 dll 中 void EchoTest()
的工作。
说 lib 相当于一个头文件没错,但是说 “是对实现部分(.dll文件)的导出部分的声明” 就有问题了。你可以说lib中是解析那些dll来实现这个lib,而不能说只是声明,因为 lib既然是obj的合集,如果只是声明何来的编译单元,又何来的obj? 显然是博主认识不太清。
这个真心不理解,dll 是运行时装载的,什么时候可以编译的时候来抽取了???????
另外,该篇博主搞了很多,诸如 “静态编译”,”动态编译”,”静态lib”,”动态lib” 之流的概念。先不说博主是否完全理解这些概念,就连有没有这些概念都是一个问题。实可谓一本正经的胡说~~~
网上找了一些资料,越来越觉得这些底层的知识,需要看专业书籍,正因为网上的资料良莠不齐,我们甄选的时候更需要带批判的眼光看问题
标签:
原文地址:http://blog.csdn.net/scythe666/article/details/51278638