使用C ++ 11/14正确定义DLL接口

时间:2015-08-19 06:57:05

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

我已经多次读过,在DLL边界之外传递STL对象(如vector和string)是不好的做法,因为不同的编译器版本可以为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问题是如何解决的?有没有现代化的开源库我可以看一下?

2 个答案:

答案 0 :(得分: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基本级别的工作原理。

答案 1 :(得分:0)

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