我能将std::string传递给DLL吗?

12

我将一个代码片段分离到一个 DLL 中,因为它会经常更新,这样部署会更容易。

但是我对于可以在 DLL 中做什么和不能做什么有疑问。

  1. 我能否把 std:string 或者 CString 传递给 DLL
  2. 我能否传递一个指向带有 std::string 成员struct 的指针,并在 DLL 中填充它?
  3. DLL 能否返回在其中分配的结构体指针?它是否有效?我可以之后删除它吗?
  4. 传递什么比较好,std::String 还是 Cstring

谢谢!

2个回答

31
您有一个选择:
  • 紧密耦合的 DLL:该 DLL 使用与应用程序完全相同的编译器版本、打包和调用约定设置、库选项,并且两者都动态链接到运行时库(/MD 编译器选项)。这样可以传递对象,包括 STL 容器,从应用程序内部分配 DLL 对象,从另一个模块派生基类,几乎可以做任何不使用 DLL 的事情。缺点是您不能独立于主应用程序部署 DLL。两者必须一起构建。DLL 只是为了提高进程启动时间和工作集,因为应用程序在加载 DLL 之前就可以开始运行(使用 /delayload 链接器选项)。构建时间也比单个模块更快,特别是使用整体程序优化时。但是优化不会跨应用程序-DLL 边界进行。任何非微小改动仍然需要重建两者。

  • 松散耦合:应用程序不依赖于 DLL 定义的对象的类布局。只使用高度兼容的数据类型:原始类型、指针、函数指针和由这些元素组成的用户定义类型。类继承自定义一个基类,定义接口并且没有数据成员和非虚函数(这意味着没有构造函数和不共享标准库对象,如 std::stringCString)。所有分配和对象创建必须通过工厂函数完成。内存必须从分配它的模块释放。代码和数据是分开的。头文件明确说明了每个导出函数的调用约定和允许跨模块边界的每个结构的打包方式。优点是 DLL 和应用程序可以完全独立地更新。您可以使用新的运行时库、新的编译器版本甚至是完全不同的语言重新构建一个,而不必触及另一个。

我总是建议使用松散耦合的方法。


2
@bratao:你不能从主应用程序中删除在DLL中分配的内存。 DLL可以使用std::string,但它与应用程序中的std::string不同。 你不能在应用程序和DLL之间传递std::string,而是像Mark建议的那样传递char* - Ben Voigt
1
@Ela782:作用域资源管理类不适合跨越边界传递。你可以在客户端使用它们来管理通过边界传递的原始指针(确保设置一个自定义删除器,调用正确的释放函数,因为默认的删除器在这种情况下不起作用),但智能指针对象不能跨越边界。 - Ben Voigt
2
@Ela782:你需要查看保证细节,但我不认为它会显著改善情况。ABI兼容性意味着如果您的基本类型聚合体定义未更改,则可以将这些特定编译器发布视为满足“完全相同的编译器版本”要求。但是,namespace std中类的定义仍然可能会发生变化(必须,因为C++标准委员会会更改要求),因此那些仍不能跨模块边界使用。 - Ben Voigt
1
听起来你决定要冒险而不是采用一个稳健的方法。即使在同一编译器供应商的某些版本之间具有完全的ABI兼容性和库不变性,我仍然认为这是紧密耦合的,因为你剥夺了消费者使用你的DLL时选择编译器的权利。 - Ben Voigt
1
@AndriiZymohliad:OpenCV是开源的,因此紧密耦合并不会剥夺编译器的选择...即使您为几个平台分发预构建的二进制文件,使用不同平台的开发人员也可以重新编译OpenCV的源代码。我的回答涉及以二进制(DLL或.so)格式分发的库。 - Ben Voigt
显示剩余8条评论

4

如果基于模板来传递DLL的任何内容,那么存在危险。编译器选项可能会影响对象的布局,而模板类无法限制为单个编译单元;其中一些将分发给调用模块。

对于字符串,我会传递const char *(或const wchar_t *const TCHAR *),并在接口的另一侧进行转换为std::stringCString


1
不仅仅是模板,任何具有内联成员函数的类都会创建布局依赖关系。 - Ben Voigt
@Ben,确实如此。但是模板按定义是内联的,因此对它们的警告要加倍。 - Mark Ransom
谢谢你的想法,我会去做的! - bratao

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接