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

And*_*dré 62 c++ dll stl std c++11

在导出包含与visual studio警告C4251相关的stl类的类之前,有一些问题:例如这个问题或这个问题.我已经阅读过UnknownRoad的优秀解释.

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

如何在我的dll界面中使用stl-classes?什么是常见做法?

Joh*_*ing 89

在进一步阅读之前,请记住一件事:我的答案来自于编写可移植代码的观点,可以在由不同编译器编译的模块组成的应用程序中使用.这可以包括同一编译器的不同版本甚至不同的补丁级别.

如何在我的dll界面中使用stl-classes?

答: 你经常不能1.

原因: STL是一个代码库,而不是像DLL这样的二进制库.它没有一个可以保证在任何地方使用它的ABI.实际上,STL确实代表" 标准模板库",但除了标准之外,这里的关键操作词是模板.

标准定义了每个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]
};
Run Code Online (Sandbox Code Playgroud)

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

答案是,有许多不同的方式.有些编译器可能使用size_t成员变量来保存大小,使用a 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;) }
Run Code Online (Sandbox Code Playgroud)

...而带有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(); }
Run Code Online (Sandbox Code Playgroud)

我不是说他们这个样子,我说他们可能.这里的要点是实际的实现是依赖于平台的,你真的无法知道它在其他任何地方会是什么.

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

class MyGizmo
{
public:
  std::string name_;
};
Run Code Online (Sandbox Code Playgroud)

什么是sizeof(MyGizmo)

假设我上面提出的实现,在MSVC10下它将会是sizeof(char*),但在SP1下它将是sizeof(vector<char>).如果您在使用DLL的VC10 SP1中编写应用程序,则对象的大小将与实际大小不同.二进制接口已更改.


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


1:" 您经常不能 "当您完全控制工具链和库时,您实际上可以以相当大的可靠性导出标准库组件或任何其他代码库组件(例如Boost).

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

  • 感谢John的出色解释.我知道ABI不符合标准.使用一个编译器构建所有库是否足够?或者所有stl成员变量都应该在pimpl指针后面? (3认同)
  • @Andre:是的,使用相同的编译器构建所有内容应该足够了.当我说"在你进一步阅读之前记住一件事......"时,这就是我所暗示的那种...... (3认同)