码迷,mamicode.com
首页 > 其他好文 > 详细

从一个logger引发的lib和dll探讨

时间:2016-04-29 01:42:53      阅读:271      评论:0      收藏:0      [点我收藏+]

标签:

一. 问题来由

项目写了个logger,本来是代码的,大家单独包含都可以使用,但是后来项目整合,每个人的部分打成lib,而前端将logger打包一起编译成lib,后台按道理应该是不用包含 .cpp 文件也可以用到logger,因为如果后台也包含并编译,应该会和前端的编译单元中的 logger 重定义。

但是奇怪的是,非但不重定义,反而这边获取不到 io 流 (流是静态变量),匪夷所思,最后是采用命名空间解决了。

虽然那个问题没有完美解决,但是我决定好好搞一下 lib 和 dll ,因为仅就 logger 来说:

好处1 这个项目中,日志模块是完全独立的模块,从设计的角度来看,本来就应该独立开来打包,然后大家就可以一起用这个包,实现了松耦合;

好处2 logger 打成 lib 或者 dll,大家就可以像使用一些api一样松松使用,不仅用于这个项目,以后实验室的项目都可以使用。

另外,也可以彻底搞一下动态库和静态库。

二. 从最简单的定义说起

最简单的定义,也是最具有可信信的:

lib 和 dll

一个 lib 文件是obj文件的集合。

当然,其中还夹杂着其他一些辅助信息,目的是为了让编译器能够准确找到对应的 obj 文件。我们可以通过tlib.exe(在tc2.0下的根目录)来对lib文件进行操作

dll 是运行时装载的函数实现代码,个人认为是部分代码段汇编对应的二进制码。

静态编译与动态编译

注: 本人对于静态编译和动态编译的概念几经查证,尚无定论,先留白在此,而后查证后更新。

如下解释只是个人理解。

动态编译: 可执行文件需要附带一个的动态链接库,动态链接库在运行时加载

静态编译: 将附加的静态链接库链接进最后的可执行文件,是编译期所做的事情

三. 静态链接库版本 logger

本人编译环境为: win7 vs2013,静态链接库为lib

1 生成 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 就链接是没有意义的。

2 外部使用 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 放到可执行程序目录,或者放到环境变量中

然后就可以正常使用了。

四. 动态库链接库版本 logger

因为是 win 平台,所以生成动态库是 dll

1 生成 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 是给出暴露接口的函数入口地址

技术分享

2 外部使用 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

问题 1 动态lib相当于一个h文件

文章反复提到 “动态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? 显然是博主认识不太清。

问题 2 静态编译可抽取 dll 中使用部分到最终可执行文件中,从而实现对 dll 的脱离

这个真心不理解,dll 是运行时装载的,什么时候可以编译的时候来抽取了???????

另外,该篇博主搞了很多,诸如 “静态编译”,”动态编译”,”静态lib”,”动态lib” 之流的概念。先不说博主是否完全理解这些概念,就连有没有这些概念都是一个问题。实可谓一本正经的胡说~~~

小结

网上找了一些资料,越来越觉得这些底层的知识,需要看专业书籍,正因为网上的资料良莠不齐,我们甄选的时候更需要带批判的眼光看问题


参考资料

[1] http://blog.csdn.net/lushuner/article/details/25048465

[2] http://bbs.csdn.net/topics/390177392

从一个logger引发的lib和dll探讨

标签:

原文地址:http://blog.csdn.net/scythe666/article/details/51278638

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!