【问题标题】:Correctly defining DLL-interfaces with C++11/14使用 C++11/14 正确定义 DLL 接口
【发布时间】:2015-11-12 08:07:26
【问题描述】:

我多次读过,在 DLL 边界之外传递向量和字符串等 STL 对象是不好的做法,因为不同的编译器版本可以为 STL 对象生成不同的代码。因此,您应该设计一个 C 风格的接口,并且根本不传递 STL 对象。但是,我仍然有一些不清楚的地方:

1. DLL 的“边界”是什么?

是否正确地说,边界是代码在 DLL 端被 编译 的地方?如果我在 DLL 中定义一个 .h 文件(例如编写工厂类)并在不同的项目中使用该头文件怎么办?那个 .h 文件是在 DLL 的边界之内还是之外,为什么?

2。 DLL 中包含什么?

假设我有一个类 Foo:

class Foo
{
public:
    __declspec(dllexport) void f1(); //instantiates v1 inside function
private:
    unique_ptr<vector<int>> v1 = nullptr;
}

如果我只用 __declspec(dllexport) 标记函数 f1(),则只有这个函数应该包含在 DLL 中。如果 DLL 中不包含 v1,f1() 中的代码如何知道 v1 是什么?

3.使用 unique_ptr 将对象传递出 DLL 边界

我几乎每次在我的项目中都使用 unique_ptr。据我了解,从 DLL 返回 unique_ptr 是不好的做法,因为 unique_ptr 是一个 STL 对象。如何在 DLL 中实例化一个对象并返回一个 unique_ptr 给它?

4.为什么定义接口或使用 PIMPL 有助于定义 DLL 接口?

我仍然需要将我的 STL 类转换为 C 风格的对象。在使用 DLL 的项目中,我不得不以某种方式再次将 C 风格的对象包装在 STL 类中。在这种情况下,我看不到使用接口或 PIMPL 的任何优势。 另外,如果我定义一个接口(具有纯虚函数的类),这与在我的类中使用 __declspec(dllexport) 声明函数的效果不一样吗?

class IFoo
{
public:
    virtual ~IFoo() = 0 {};
    virtual void f1() = 0;
}
class Foo : public IFoo
{
public:
    void f1();
    //__declspec(dllexport) void f1(); //why use an interface if I can just declare the functions like this?
}

如何在现代 C++ 11/14 库中解决 DLL-STL 问题?是否有任何现代开源库可供我查看?

【问题讨论】:

  • On 选项当然不是混合编译器版本或为每个支持的编译器构建单独的库。 Boost does that
  • 这可能是最简单的解决方案,我正在考虑这样做,因为确保可移植 DLL 似乎是一场真正的斗争。一般来说,这对于商业项目也可以接受吗?

标签: c++ c++11 dll stl c++14


【解决方案1】:

很遗憾,不同编译器的 STL 类型并不一致。甚至不同版本的 Visual Studio 也有差异。

边界是编译代码的地方。如果您在库的头文件中有实现,则用于编译 EXE 的编译器将编译代码。这可能非常糟糕,因为 EXE 中的代码认为数据与 DLL 中的代码认为数据不同。 (您需要注意这样的差异,特别是如果您在结构定义中有 #ifs 并且您需要明确包装)。

唯一确定的方法是定义您自己的所有类型(注意打包)而不使用 STL。这是 DLL 库通常做的事情。

接口可以使用户动态链接到库。使用 __declspec(dllexport) 需要静态链接;也就是说,EXE 必须链接到编译 DLL 时生成的 .lib 才能访问所有功能。这意味着您无法在不重新编译 EXE 的情况下更新 DLL(可能 - 在某些情况下您可以摆脱这种情况,但这不是一个好主意)。

只要不更改接口,您就可以通过动态链接更新 DLL 或向 DLL 添加功能,而无需重新链接 EXE。 EXE 可能会调用 DLL 上的 LoadLibrary() 和 GetProcAddress() 来访问一个返回接口的函数。包括作为参数传递的数据类型在内的其他所有内容都是接口(即仅包含纯虚函数)或简单结构。这就是 COM 的基本级别的工作原理。

【讨论】:

    【解决方案2】:

    要回答问题 2,当您声明 __declspec(dllexport) 时,您是在声明这是 DLL 接口的一部分 - 加载 DLL 的组件可以访问该接口。没有__declspec(dllexport) 声明的任何内容都应该存在于 DLL 中,但不能被外部组件调用/使用。

    【讨论】:

    • 所以基本上所有代码都包含在 .dll 中,但只有用 __declspec(dllexport) 声明的代码是公开的,谢谢!
    猜你喜欢
    • 1970-01-01
    • 2019-02-23
    • 1970-01-01
    • 2016-08-17
    • 1970-01-01
    • 2017-03-20
    • 1970-01-01
    • 1970-01-01
    • 2013-07-11
    相关资源
    最近更新 更多