【问题标题】:c++ plugin : Is it ok to pass polymorphic objects?c++ 插件:可以传递多态对象吗?
【发布时间】:2015-02-12 23:20:35
【问题描述】:

当使用动态库时,我知道我们应该只跨边界传递普通的旧数据结构。那么我们可以传递一个指向 base 的指针吗?

我的想法是应用程序和库都可以知道一个通​​用接口(纯虚拟方法,= 0)。 该库可以实例化该接口的子类型, 并且应用程序可以使用它。

例如,下面的 sn-p 安全吗?

// file interface.h

class IPrinter{
    virtual void print(std::string str) = 0;
 };

-

// file main.cpp

int main(){
    //load plugin...
    IPrinter* printer = plugin_get_printer();
    printer->print( std::string{"hello"} );
}

-

// file plugin.cpp (compiled by another compiler)

IPrinter* plugin_get_printer(){
    return new PrinterImpl{};
}

【问题讨论】:

  • 如果你在 Windows 上,你基本上是在重新设计 COM。它以足够优雅但更重要的是有据可查的方式解决了您正在谈论的问题。不过,我对非 Windows 平台的了解还不够多,无法对此发表评论。

标签: c++ plugins binary system


【解决方案1】:

这个sn-p不安全:

  • DLL 边界的两侧不使用相同的编译器。这意味着name mangling(用于函数名称)和vtable 布局(用于虚函数)可能不同(特定于实现。

  • 两侧的堆也可能以不同的方式管理,因此如果不在 DLL 中删除对象,您将面临与删除相关的风险。

这个article 很好地展示了二进制兼容接口的主要挑战。

但是,您可以将指针传递给镜像的另一侧,作为 POD 的一部分,只要另一部分自己不使用它(例如:您的应用程序将指向配置对象的指针传递给DLL。稍后另一个 DLL 函数将该指针返回到您的应用程序。然后您的应用程序可以按预期使用它(至少如果它不是指向不再存在的本地对象的指针)。

【讨论】:

  • 感谢您的回答。这篇文章很有趣!关于 sn-p std::vector<string> *pVec = getVectorFromDll();,我读到:“如果这是一个实现了自己的排序方法的容器,例如 std::list,调用容器自己的排序方法将是安全的,因为它会使用DLL中的正确实现。” 是否意味着我们可以使用在另一端创建的非多态对象?
  • 不,这句话是关于在 DLL 集合上的 EXE 中使用标准(模板)算法:EXE 编译器的假设可能对 DLL 端无效!它说如果你从集合中调用一个方法(也就是用 DLL 编译),你就不会有这种混淆。关于使用非多态对象,请阅读“非 POD 类型”部分:它很可能会失败,除非您在双方都使用完全相同的编译器。然后它可以工作,但需要一些仔细的一致性检查(另见:stackoverflow.com/questions/3564985/…
  • @Chistophe,请参阅这个相关的 SO 问题:stackoverflow.com/questions/28500533/…
  • 这个答案是一个很好的 C++03 答案。在 C++11 中,POD 作为一个概念基本上已经过时了,应该说标准布局。
【解决方案2】:

你的类中存在虚函数意味着你的类将有一个 vtable,不同的编译器实现 vtable 的方式不同。

因此,如果您在 DLL 调用中使用具有虚拟方法的类,而另一端使用的编译器与您正在使用的编译器不同,则结果很可能是严重的崩溃。

在您的情况下,由 DLL 创建的 PrinterImpl 将以某种方式构造一个 vtable,但您的 main() 中的 printer->print() 调用将尝试以不同的方式解释 IPrinter 的 vtable为了解决print()方法调用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-05
    • 1970-01-01
    • 2017-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-21
    相关资源
    最近更新 更多