zhiqiang
V2EX  ›  C

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

  •  
  •   zhiqiang · Oct 18, 2018 · 5211 views
    This topic created in 2787 days ago, the information mentioned may be changed or developed.

    我要做的事情很简单,这个单例是 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函数。

    18 replies    2018-10-23 08:36:11 +08:00
    justou
        1
    justou  
       Oct 18, 2018
    get_logger 需要导出, 这样声明:

    extern "C" _declspec(dllexport) Logger* get_logger();
    icedx
        2
    icedx  
       Oct 18, 2018
    如果用 mingw 编译大概可以
    zhiqiang
        3
    zhiqiang  
    OP
       Oct 18, 2018
    @justou 我加了一些 dllexport dllimport 还是不行。
    justou
        4
    justou  
       Oct 18, 2018   ❤️ 1
    不清楚你具体怎么操作的, 我建了个测试工程, 你可以试下

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

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

    如果用 windows api 的 LoadLibrary 函数则不需要那个 some.lib, 这样需要自己手动找符号对应的地址.
    justou
        5
    justou  
       Oct 18, 2018
    owt5008137
        6
    owt5008137  
       Oct 19, 2018 via Android
    elf 里的堆是共享的,但是 pe 里 dll 的堆是独立的。所以唯一的方法是 dll 里导出一个函数,然后只由这个 dll 分配对象。不能写到.h 里,相应的.cpp 也不要链接进 exe 或者其他 dll
    wutiantong
        7
    wutiantong  
       Oct 19, 2018
    @zhiqiang 哎?是不是 fit@thu 的 zhiqiang ?
    arzterk
        8
    arzterk  
       Oct 19, 2018
    windows 头文件导出类需要用宏区分 dllimport、dllexport 的,不然编译不是库编不过就是链接代码出问题
    z4none
        9
    z4none  
       Oct 22, 2018
    感觉你需要的是 #pragma data_seg
    zhiqiang
        10
    zhiqiang  
    OP
       Oct 22, 2018
    @wutiantong haha,你也在这里混啊。现在在哪呢
    zhiqiang
        11
    zhiqiang  
    OP
       Oct 22, 2018
    @justou 谢谢。

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

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

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

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

    Linux 系统上我上面的示例代码就可以做到 。关键在于 Linux 可以在运行时进行函数链接。
    zhiqiang
        18
    zhiqiang  
    OP
       Oct 23, 2018
    @wutiantong 不是这个~~~
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   3148 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 79ms · UTC 03:57 · PVG 11:57 · LAX 20:57 · JFK 23:57
    ♥ Do have faith in what you're doing.