关于 “将静态库改成动态库” 的思考

在编写任何一个框架(c++ 库) 的时候,需要想好编译配置:

  • 是否需要跨平台:
    操作系统抽象层(条件编译,系统时间,条件变量,锁,文件读写,线程等等)
  • 静态库:
    头文件&库文件,区分好Debug/Release…
  • 动态库:
    考虑好暴露哪些接口,需要在每一个暴露的接口处添加 DLL_PUBLIC 宏,所有暴露的接口都最好是纯虚函数形式暴露等…
  • 动态库的使用分为隐式显式:在 Windows 平台,隐式地链接动态库(在编译的时候)需要通过 “import library” xxx.lib 和一个 xxx.dll; 而在 Linux 平台则只需要 xxx.so 这一个库, see link。显示的使用动态库(在运行的时候)就是通过 LoadLibraryLoadLibraryEx 直接打开*.dll文件(linux: dlopen 打开 *.so 文件)。

This is a typical way to export a DLL API for Windows and still support Linux:

#if defined _WIN32 || defined __CYGWIN__ || defined __MINGW32__
  #ifdef BUILDING_DLL
    #ifdef __GNUC__
      #define DLL_PUBLIC __attribute__ ((dllexport))
    #else
      #define DLL_PUBLIC __declspec(dllexport) // Note: actually gcc seems to also supports this syntax.
    #endif
  #else
    #ifdef __GNUC__
      #define DLL_PUBLIC __attribute__ ((dllimport))
    #else
      #define DLL_PUBLIC __declspec(dllimport) // Note: actually gcc seems to also supports this syntax.
    #endif
  #endif
  #define DLL_LOCAL
#else
  #if __GNUC__ >= 4
    #define DLL_PUBLIC __attribute__ ((visibility ("default")))
    #define DLL_LOCAL  __attribute__ ((visibility ("hidden")))
  #else
    #define DLL_PUBLIC
    #define DLL_LOCAL
  #endif
#endif

In the DLL source:

#define BUILDING_DLL
#include "module.h"

DLL_PUBLIC int exported_func()  // 在dll中暴露
{
    /* do something useful */
    return 0;
}

DLL_LOCAL int hidden_func()  // 不会在dll中暴露
{
    /* do something useful */
    return 0;
}

当然,在export一个c++函数时,需要考虑加上 extern "C",否则, Name mangling 会导致不同编译器(或者不同版本的编译器)生成不同的函数名,导致 GetProcAddress (linux: dlopen)找不到对应名字的函数,当然, 这样也就不能使用重载函数了。

在暴露某一个类的时候,该类的所有成员都会暴露,如果使用了 标准库的某些成员,会导致很多编译错误,可以写成这样:

class DLL_PUBLIC ExportedClass
{
private:
    std::vector<int> *_integers;
public:
    ExportedClass()
    {
        _integers = new std::vector<int>();
    }
    ~ExportedClass()
    {
        delete _integers;
    }
};

注意,在Window平台上 dll 和你的 exe 文件可能链接不同版本的 C runtime 库,他们会使用不同的文件描述符、环境变量、本地语言设置,以及 不同的堆管理器,所以在哪个堆上new出来对象的就要在哪个堆上delete.

See DLL Boundaries: https://msdn.microsoft.com/en-us/library/ms235460.aspx

所以 stl 对象最好不要编译进dll,或者按照该link 处理:https://stackoverflow.com/questions/4145605/stdvector-needs-to-have-dll-interface-to-be-used-by-clients-of-class-xt-war

总之,在设计接口的时候,考虑 不要 在基类中放置任何成员变量,以方便暴露出去,如下:

A C++ abstract interface (i.e., a C++ class that contains only pure virtual methods and no data members) tries to get the best of both worlds: a compiler independent clean interface to an object, and a convenient object oriented way of method calls. All that is required to do is to provide a header file with an interface declaration and implement a factory function that will return the newly created object instances. Only the factory function has to be declared with the __declspec(dllexport/dllimport) specifier. The interface does not require any additional specifiers.

// The abstract interface for Xyz object.
// No extra specifiers required.
struct IXyz
{
    virtual int Foo(int n) = 0;
    virtual void Release() = 0;
};

// Factory function that creates instances of the Xyz object.
extern "C" XYZAPI IXyz* APIENTRY GetXyz();

In the above code snippet, the factory function GetXyz is declared as extern “C”. It is required in order to prevent the mangling of the function name. So, this function is exposed as a regular C function, and can be easily recognized by any C-compatible compiler. This is how the client code looks like, when using an abstract interface:

#include "XyzLibrary.h"

...
IXyz* pXyz = ::GetXyz();

if(pXyz)
{
    pXyz->Foo(42);

    pXyz->Release();
    pXyz = NULL;
}

原理如图:
在这里插入图片描述
copy from: https://www.codeproject.com/Articles/28969/HowTo-Export-C-classes-from-a-DLL

另外,编译的时候需要考虑好 link dependency, check link: http://www.kaizou.org/2015/01/linux-libraries.html
如果 一个 static lib 依赖 一个 shared lib, 当这个 app link 的 static lib 的时候,需要同时加上 static lib 和 shared lib 链接选项。

To summarize, when linking an executable against:

a static library, you need to specify all dependencies towards other shared libraries this static library depends on explicitly on the link command.

a shared library, you don’t need to specify dependencies towards other shared libraries this shared library depends on, but you may need to specify the path to these libraries on the link command using the -rpath/-rpath-link options.

关于 Dynamic Linking 的过程,可以看一下 Load-time RelocationPIC(强烈推荐读一读):

  1. https://eli.thegreenplace.net/2011/08/25/load-time-relocation-of-shared-libraries
  2. https://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/

Other links:

  • Link1: https://www.codeproject.com/Articles/28969/HowTo-Export-C-classes-from-a-DLL
  • Link2: http://www.cplusplus.com/forum/general/120142/
  • Link3: https://stackoverflow.com/questions/22797418/how-do-i-safely-pass-objects-especially-stl-objects-to-and-from-a-dll?noredirect=1&lq=1
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值