从 DLL 导出包含 `std::` 对象(向量、映射等)的类

时间:2022-12-30
本文介绍了从 DLL 导出包含 `std::` 对象(向量、映射等)的类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试从包含 std::vectorsstd::strings 等对象的 DLL 中导出类 - 整个类被声明为 DLL 导出通过:

class DLL_EXPORT FontManager {

问题是,对于复杂类型的成员,我收到此警告:

<块引用>

warning C4251: 'FontManager::m__fonts' : class 'std::map<_Kty,_Ty>'需要有 dll 接口供FontManager"类的客户端使用和[_Kty=std::string,_Ty=tFontInfoRef]

即使我没有更改成员变量本身的类型,我也可以通过将以下前向类声明放在它们之前来删除一些警告:

模板类 DLL_EXPORT std::allocator;模板类 DLL_EXPORT std::vector>;std::vectorm_glyphProviders;

看起来像前向声明injects"编译成员时的 DLL_EXPORT 但它安全吗?
当客户端编译这个头文件并在他身边使用 std:: 容器时,它真的改变了什么吗?
它是否会在未来使用这种容器 DLL_EXPORT(并且可能不是内联的)?
并且它真的解决了警告试图警告的问题吗?

这个警告是我应该担心的,还是最好在这些结构的范围内禁用它?
客户端和 DLL 将始终使用相同的库和编译器集构建,而这些只是头文件类...

我使用的是带有标准 STD 库的 Visual Studio 2003.


更新

我想更多地针对您,因为我看到答案很笼统,这里我们谈论的是 std 容器和类型(例如 std::string)——也许问题真的很重要是:

我们能否通过相同的库头禁用对客户端和 DLL 可用的标准容器和类型的警告,并像对待 int 或任何其他内置函数一样对待它们?类型?(它似乎在我这边工作正常)
如果是这样,我们应该在什么条件下才能做到这一点?

或者是否应该禁止使用此类容器,或者至少要格外小心以确保不会将赋值运算符、复制构造函数等内联到 DLL 客户端中?

总的来说,我想知道您是否觉得设计具有此类对象的 DLL 接口(例如使用它们将内容作为返回值类型返回给客户端)是否是一个好主意,以及为什么,我会喜欢有高水平"此功能的接口...
也许最好的解决方案是 Neil Butterworth 建议的 - 创建一个静态库?

解决方案

当您从客户端访问类中的成员时,您需要提供一个 DLL 接口.DLL 接口意味着编译器在 DLL 本身中创建函数并使其可导入.

因为编译器不知道 DLL_EXPORTED 类的客户端使用哪些方法,所以它必须强制所有方法都是 dll 导出的.它必须强制客户端可以访问的所有成员也必须 dll 导出它们的函数.当编译器警告您未导出的方法以及客户端的链接器发送错误时,就会发生这种情况.

不是每个成员都必须用 dll-export 标记,例如客户无法接触的私人成员.在这里您可以忽略/禁用警告(注意编译器生成的 dtor/ctors).

否则成员必须导出他们的方法.使用 DLL_EXPORT 向前声明它们不会导出这些类的方法.您必须在其编译单元中将相应的类标记为 DLL_EXPORT.

它归结为...(对于非 dll 可导出成员)

  1. 如果您的成员不能/不能被客户使用,请关闭警告.

  2. 如果您有必须由客户端使用的成员,请创建一个 dll 导出包装器或创建间接方法.

  3. 要减少外部可见成员的数量,请使用PIMPL idiom.

<小时>

模板类 DLL_EXPORT std::allocator;

这确实在当前编译单元中创建了模板特化的实例化.所以这会在dll中创建std::allocator的方法并导出对应的方法.这不适用于具体类,因为这只是模板类的实例化.

I'm trying to export classes from a DLL that contain objects such as std::vectors and std::strings - the whole class is declared as DLL export through:

class DLL_EXPORT FontManager {

The problem is that for members of the complex types I get this warning:

warning C4251: 'FontManager::m__fonts' : class 'std::map<_Kty,_Ty>' needs to have dll-interface to be used by clients of class 'FontManager'
      with
      [
          _Kty=std::string,
          _Ty=tFontInfoRef
      ]

I'm able to remove some of the warnings by putting the following forward class declaration before them even though I'm not changing the type of the member variables themselves:

template class DLL_EXPORT std::allocator<tCharGlyphProviderRef>;
template class DLL_EXPORT std::vector<tCharGlyphProviderRef,std::allocator<tCharGlyphProviderRef> >;
std::vector<tCharGlyphProviderRef> m_glyphProviders;

Looks like the forward declaration "injects" the DLL_EXPORT for when the member is compiled but is it safe?
Does it really change anything when the client compiles this header and uses the std:: container on his side?
Will it make all future uses of such a container DLL_EXPORT (and possibly not inline)?
And does it really solve the problem that the warning tries to warn about?

Is this warning anything I should be worried about or would it be best to disable it in the scope of these constructs?
The clients and the DLL will always be built using the same set of libraries and compilers and those are header only classes...

I'm using Visual Studio 2003 with the standard STD library.


Update

I'd like to target you more though as I see the answers are general and here we're talking about std containers and types (such as std::string) - maybe the question really is:

Can we disable the warning for standard containers and types available to both the client and the DLL through the same library headers and treat them just as we'd treat an int or any other built-in type? (It does seem to work correctly on my side)
If so would should be the conditions under which we can do this?

Or should maybe using such containers be prohibited or at least ultra care taken to make sure no assignment operators, copy constructors etc will get inlined into the DLL client?

In general I'd like to know if you feel designing a DLL interface having such objects (and for example using them to return stuff to the client as return value types) is a good idea or not and why, I'd like to have a "high level" interface to this functionality...
Maybe the best solution is what Neil Butterworth suggested - creating a static library?

解决方案

When you touch a member in your class from the client, you need to provide a DLL-interface. A DLL-interface means, that the compiler creates the function in the DLL itself and makes it importable.

Because the compiler doesn't know which methods are used by the clients of a DLL_EXPORTED class it must enforce that all methods are dll-exported. It must enforce that all members which can be accessed by clients must dll-export their functions too. This happens when the compiler is warning you of methods not exported and the linker of the client sending errors.

Not every member must be marked with with dll-export, e.g. private members not touchable by clients. Here you can ignore/disable the warnings (beware of compiler generated dtor/ctors).

Otherwise the members must export their methods. Forward declaring them with DLL_EXPORT does not export the methods of these classes. You have to mark the according classes in their compilation-unit as DLL_EXPORT.

What it boils down to ... (for not dll-exportable members)

  1. If you have members which aren't/can't be used by clients, switch off the warning.

  2. If you have members which must be used by clients, create a dll-export wrapper or create indirection methods.

  3. To cut down the count of externally visible members, use approaches such as the PIMPL idiom.


template class DLL_EXPORT std::allocator<tCharGlyphProviderRef>;

This does create an instantiation of the template specialization in the current compilation unit. So this creates the methods of std::allocator in the dll and exports the corresponding methods. This does not work for concrete classes as this is only an instantiation of template classes.

这篇关于从 DLL 导出包含 `std::` 对象(向量、映射等)的类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持html5模板网!

上一篇:发布版本与调试版本的运行方式不同的一些原因 下一篇:如何在 Visual Studio 2008 中启动新的 CUDA 项目?

相关文章

最新文章