如何在我的 dll 接口或 ABI 中使用标准库 (STL) 类?

问题描述 投票:0回答:1

之前有一些关于导出包含与 Visual Studio 警告 C4251 相关的 stl 类的类的问题:这个问题或者这个问题。 我已经阅读了 UnknownRoad 上的精彩解释。

盲目禁用警告似乎有点危险,尽管这可能是一种选择。包装所有这些 std 类并导出它们也不是真正的选择。它毕竟被称为标准模板库...也就是说,人们想要为这些标准类提供一个接口。

如何在 dll 接口中使用 stl 类?常见做法有哪些?

c++ dll c++11 stl std
1个回答
99
投票

在进一步阅读之前请记住一件事:我的答案是从编写可移植代码的角度出发的,这些代码可以在由不同编译器编译的模块组成的应用程序中使用。这可以包括同一编译器的不同版本甚至不同补丁级别。

如何在 dll 接口中使用 stl 类?

答案:你经常做不到1

原因:STL是一个代码库,而不是像DLL那样的二进制库。它没有一个 ABI 可以保证无论您在哪里使用它都是相同的。事实上,STL 确实代表“Standard Template Library”,但除了 Standard 之外,这里的一个关键操作词是 Template

该标准定义了每个STL类需要提供的方法和数据成员,并定义了这些方法的用途;但仅此而已。特别是,该标准没有指定编译器编写者应如何实现标准定义的功能。编译器编写者可以自由地提供 STL 类的实现,该实现添加标准中未列出的成员函数和成员变量,只要标准中定义的那些成员仍然存在并按照标准执行操作即可。 也许可以举个例子。 basic_string类在标准中定义为具有某些成员函数和变量。标准中的实际定义几乎有 4 页,但这里只是其中的一个片段:

namespace std { template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> > class basic_string { [snip] public: // 21.3.3 capacity: size_type size() const; size_type length() const; size_type max_size() const; void resize(size_type n, charT c); void resize(size_type n); size_type capacity() const; void reserve(size_type res_arg = 0); void clear(); bool empty() const; [snip] };

考虑 

size()
length()

成员函数。标准中没有任何内容指定用于保存此信息的成员变量。事实上,根本没有定义任何成员变量,甚至没有定义保存字符串本身。那么这是如何实现的呢?

答案是,有很多不同的方式。某些编译器可能使用 
size_t
成员变量来保存大小,并使用

char*

来保存字符串。另一种可能使用指向保存该数据的其他数据存储的指针(这可能是引用计数实现中的情况)。事实上,同一编译器的不同版本甚至“补丁级别”可能会改变这些实现细节。你不能依赖他们。因此,MSVC 10 的实现可能如下所示:

namespace std {
  template<class charT, class traits = char_traits<charT>,
    class Allocator = allocator<charT> >
  class basic_string {
[snip]
char* m_pTheString;
};

size_t basic_string::size() const { return strlen(m_pTheString;) }
 ...而带有 SP1 的 MSVC 10 可能看起来像这样:
namespace std {
  template<class charT, class traits = char_traits<charT>,
    class Allocator = allocator<charT> >
  class basic_string {
[snip]
vector<char> m_TheString;
};

size_t basic_string::size() const { return m_TheString.size(); }

我并不是说它们
确实

看起来像这样,我是说它们可能会这样。这里的要点是实际的实现是依赖于平台的,你真的无法知道它在其他地方会是什么。

现在假设您使用 MSVC10 编写导出此类的 DLL:

class MyGizmo { public: std::string name_; }; 什么是

sizeof(MyGizmo)

假设我上面建议的实现,在 MSVC10 下它将是 
sizeof(char*)

,但在 SP1 下它将是

sizeof(vector<char>)
。如果您在 VC10 SP1 中编写使用 DLL 的应用程序,则对象的大小看起来将与实际大小不同。二进制接口已更改。

有关此问题的另一种处理方法,请参阅
C++ 编码标准
(亚马逊
链接

)第 63 期。

1:“你通常不能

”当你完全控制工具链和库时,你实际上可以相当可靠地导出标准库组件或任何其他代码库组件(例如Boost)。

根本问题是,对于源代码库,不同编译器和不同版本的库之间的大小和定义可能不同。如果您在一个环境中工作,在使用代码的任何地方都可以控制这两个方面,那么您可能不会遇到问题。例如,在一家贸易公司,所有系统都是内部编写并仅在内部使用,也许可以这样做。

© www.soinside.com 2019 - 2024. All rights reserved.