windows 下 C++如何生成跨 DLL/SO 文件的单例?

2018-10-18 20:07:43 +08:00
 zhiqiang

我要做的事情很简单,这个单例是 Log 示例,用来写 Log。用简单的单例方法:

// log.h
class Log {
    Log* instance() {
        static Log log;
        return &log;
    }
}

这样做会在每个 DLL 文件里会生成各自的 DLL 实例,不能直接这么写。

在 Linux 下我是这个做可以生成一个跨 DLL 的单例:

// in log.h
extern C {
    Log* get_logger() 
}
class Log {
    Log* instance() {
        return get_logger();
    }
}

#ifndef _USRDLL
extern C {
    Log* get_logger() {
    	static Log instance;
        return &instance;
    }
}
#endif

这种方法在 linux/gcc 下有效。但在 windows/vc2015 编译时,提示连接不到get_logger函数。

4247 次点击
所在节点    C
18 条回复
justou
2018-10-18 20:30:31 +08:00
get_logger 需要导出, 这样声明:

extern "C" _declspec(dllexport) Logger* get_logger();
icedx
2018-10-18 20:44:24 +08:00
如果用 mingw 编译大概可以
zhiqiang
2018-10-18 21:14:18 +08:00
@justou 我加了一些 dllexport dllimport 还是不行。
justou
2018-10-18 21:36:14 +08:00
不清楚你具体怎么操作的, 我建了个测试工程, 你可以试下

链接: https://pan.baidu.com/s/1Qd4FXpYwqEAcRTrRjKxq6g 提取码: 4tfu

编译时动态链接的时候除了头文件也需要一个导出符号表, 比如 some.dll(可执行代码)有个对应的 some.lib(符号表)

如果用 windows api 的 LoadLibrary 函数则不需要那个 some.lib, 这样需要自己手动找符号对应的地址.
justou
2018-10-18 21:44:47 +08:00
owt5008137
2018-10-19 08:24:38 +08:00
elf 里的堆是共享的,但是 pe 里 dll 的堆是独立的。所以唯一的方法是 dll 里导出一个函数,然后只由这个 dll 分配对象。不能写到.h 里,相应的.cpp 也不要链接进 exe 或者其他 dll
wutiantong
2018-10-19 09:45:08 +08:00
@zhiqiang 哎?是不是 fit@thu 的 zhiqiang ?
arzterk
2018-10-19 10:28:29 +08:00
windows 头文件导出类需要用宏区分 dllimport、dllexport 的,不然编译不是库编不过就是链接代码出问题
z4none
2018-10-22 10:54:43 +08:00
感觉你需要的是 #pragma data_seg
zhiqiang
2018-10-22 17:25:07 +08:00
@wutiantong haha,你也在这里混啊。现在在哪呢
zhiqiang
2018-10-22 17:31:26 +08:00
@justou 谢谢。

不过我的需求是在 exe 里定义 get_logger() 函数。所有的 dll 从 exe 里去取这个函数的实现(因为我有很多个地位平等的 dll,这些 dll 是按需加载的)。跟你给的例子恰好相反。
zhiqiang
2018-10-22 17:36:50 +08:00
@justou 你这种方案需要把 log 库做成一个 dll,这样 log 库就不能直接 header only 了。
zhiqiang
2018-10-22 17:39:28 +08:00
@owt5008137 你说的是需要把 log 库做成一个 dll,这样 log 库就不能直接 header only 了。

但在 linux 下是能实现我所需要的,同样的实现在 windows 下就不行。有些跨平台开源 log 库也是 header only 的,我还没细看他们怎么写的。
zhiqiang
2018-10-22 17:43:41 +08:00
@z4none 我去学习了一下 #pragma data_seg,这个是多个进程调用同一个 dll 文件时,共享该 dll 里面的某个变量。

这跟我说的不一样。我说的是程序同时调用多个不同的 dll 文件时,这些 dll 里的 log 对象需要引用到同一个 log 变量。
noli
2018-10-22 18:33:14 +08:00
你的需求是说,不管有多少个 dll 被加载,只要这些 dll 使用了你的 logger 那么他们所引用的 logger 必然是内存中的同一个?

要做到这一点的话必须要使用文件系统或者内核来保证。据我所知没有任何语言基本设施足以保证这一点。
wutiantong
2018-10-22 19:10:23 +08:00
@zhiqiang 我在扇贝啊
你这个问题我也搜了一下,你看看是不是这个:
https://stackoverflow.com/questions/362830/circular-dependencies-between-dlls-with-visual-studio/387380#387380
zhiqiang
2018-10-23 08:35:57 +08:00
@noli 加一个限制条件:dll 是运行在同一个进程里的。

Linux 系统上我上面的示例代码就可以做到 。关键在于 Linux 可以在运行时进行函数链接。
zhiqiang
2018-10-23 08:36:11 +08:00
@wutiantong 不是这个~~~

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/498903

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX