C++ 插件:跨边界传递对象(模拟它)

2023-12-01

由于我们不应该跨插件边界传递除普通旧数据结构[1]之外的任何其他内容,因此我想出了以下想法来传递对象:

  • 公开插件“C”接口中的所有公共方法,并在应用程序端将插件包装在一个对象中。 (参见以下示例)

我的问题是:有一个更好的方法吗 ? [编辑] 请参阅下面我的编辑,其中使用标准布局对象可能是更好的解决方案。

这是一个说明这个想法的玩具示例:

我想跨越边界传递一个 Writer :

class Writer{
     Writer();
     virtual void write(std::string) = 0;
     ~Writer(){}
};

但是,我们知道由于兼容性问题,不应该直接这样做。 这个想法是将 Writer 的接口公开为插件中的免费函数:

// plugin

extern "C"{
   Writer* create_writer(){
        return new PluginWriterImpl{}; 
   }

   void write(Writer* this_ , const char* str){
        this_->write(std::string{str});
   }

   void delete_writer(Writer* this_){
        delete this_;
   }
}

并将所有这些函数调用包装在应用程序端的包装对象中:

// app

class WriterWrapper : public Writer{
private:
      Writer* the_plugin_writer; //object being wrapped
public:
      WriterWrapper() : the_plugin_writer{ create_writer() } 
      {}

      void write(std::string str) override{
          write(the_plugin_writer,  str.c_str() );
      }

      ~WriterWrapper(){
          delete_writer(the_plugin_writer);
      }
};

这导致了很多转发功能。除了 POD 之外没有其他东西可以跨越边界,并且应用程序不知道当前 Writer 的实现来自插件这一事实。

[1] 对于二进制兼容性问题。欲了解更多信息,你可以看到这个相关的SO问题:c++ 插件:可以传递多态对象吗?


[编辑] 看来我们可以跨越边界传递标准布局。如果是这样,这样的解决方案是否正确? (可以简化吗?)

我们想要跨越边界传递 Writer :

class Writer{
     Writer();
     virtual void write(std::string) = 0;
     ~Writer(){}
};

因此,我们将从插件中传递一个标准布局对象到应用程序,并将其包装在应用程序端。

// plugin.h
struct PluginWriter{
    void write(const char* str);
};

-

// plugin_impl.cpp 

#include "plugin.h"

extern "C"{
    PluginWriter* create_writer();
    void delete_writer(PluginWriter* pw);
}

void PluginWriter::write(const char* str){
    // . . .
}

-

// app
#include "plugin.h"

class WriterWrapper : public Writer{
private:
      PluginWriter* the_plugin_writer; //object being wrapped
public:
      WriterWrapper() : the_plugin_writer{ create_writer() } 
      {}

      void write(std::string str) override{
          the_plugin_writer->write( str.c_str() );
      }

      ~WriterWrapper(){
          delete_writer(the_plugin_writer);
      }
};

但是,我担心链接器会在编译应用程序时抱怨,因为:#include plugin.h


在客户端和库端使用具有不同编译器(甚至语言)的 DLL 需要二进制兼容性(又名ABI).

无论如何谈论标准布局或 POD,C++ 标准都不保证不同编译器之间的任何二进制兼容性。 对于类成员的布局,没有全面的实现独立规则可以确保这一点(另请参见这个答案关于数据成员的相对地址)。

当然,幸运的是,在实践中许多不同的编译器在标准布局对象中使用相同的逻辑进行填充和对齐,使用 CPU 架构的具体最佳实践或要求(只要不使用打包或外来对齐编译器开关)。因此使用POD/标准布局是比较安全的(并且作为 Yakk 正确地指出:“如果你信任 Pod,你就必须信任标准布局。”)

所以你的代码可能会工作。其他替代方案,依靠 C++ 虚拟来避免名称修改问题,似乎也可以使用交叉编译器 如中所解释的本文。出于同样的原因:实际上,许多编译器在一个特定的操作系统+架构上使用一种公认的方法 用于构建他们的 vtable。但同样,这是从实践中观察到的并非绝对保证.

如果你想提供交叉编译一致性保证对于您的图书馆,那么您应该只依赖真正的保证,而不仅仅是通常的保证 实践。在 MS-Windows 上,二进制接口standard对于对象来说是COM。这里有一个全面的C++ COM 教程。它可能 有点老了,但没有其他人有这么多插图可以理解。

COM 方法当然比您的代码片段更重。但这是交叉编译器的成本,甚至是它提供的跨语言合规性保证的成本。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 插件:跨边界传递对象(模拟它) 的相关文章

  • 如何读取扩展文件属性/文件元数据

    因此 我按照教程使用 ASP net core 将文件 上传 到本地路径 这是代码 public IActionResult About IList
  • C++ 中本地类中的静态成员变量?

    我知道我们不能宣布static本地类中的成员变量 但其原因尚不清楚 那么请问有人可以解释一下吗 另外 为什么我们不能访问非static函数内部定义的变量 内部已经定义了局部类 直接在局部类成员函数中 在下面给出的代码中 int main i
  • 将内置类型转换为向量

    我的 TcpClient 类接受vector
  • 如何访问另一个窗体上的ListView控件

    当单击与 ListView 所在表单不同的表单中的按钮时 我试图填充 ListView 我在 Form1 中创建了一个方法以在 Form2 中使用 并将参数传递给 Form1 中的方法 然后填充 ListView 当我调试时 我得到了传递的
  • 在 C# 中循环遍历文件文件夹的最简单方法是什么?

    我尝试编写一个程序 使用包含相关文件路径的配置文件来导航本地文件系统 我的问题是 在 C 中执行文件 I O 这将是从桌面应用程序到服务器并返回 和文件系统导航时使用的最佳实践是什么 我知道如何谷歌 并且找到了几种解决方案 但我想知道各种功
  • 用于检查项目文件中的项目变量和引用路径的 api

    我正在研究一个 net application VS2010 与 x 没有 解和变量号这些解决方案中的项目数量 我需要检查项目属性 特定于一定数量的项目 是否同质 并且检查 验证构建期间的参考路径 有没有一个API是这样的吗 如果没有 我该
  • 如何在 Linq 中获得左外连接?

    我的数据库中有两个表 如下所示 顾客 C ID city 1 Dhaka 2 New york 3 London 个人信息 P ID C ID Field value 1 1 First Name Nasir 2 1 Last Name U
  • 使用 JNI 从 Java 代码中检索 String 值的内存泄漏

    我使用 GetStringUTFChars 从使用 JNI 的 java 代码中检索字符串的值 并使用 ReleaseStringUTFChars 释放该字符串 当代码在 JRE 1 4 上运行时 不会出现内存泄漏 但如果相同的代码在 JR
  • Rx 中是否有与 Task.ContinueWith 运算符等效的操作?

    Rx 中是否有与 Task ContinueWith 运算符等效的操作 我正在将 Rx 与 Silverlight 一起使用 我正在使用 FromAsyncPattern 方法进行两个 Web 服务调用 并且我想这样做同步地 var o1
  • 如何将整数转换为 void 指针?

    在 C 中使用线程时 我面临警告 警告 从不同大小的整数转换为指针 代码如下 include
  • 批量更新 SQL Server C#

    我有一个 270k 行的数据库 带有主键mid和一个名为value 我有一个包含中值和值的文本文件 现在我想更新表格 以便将每个值分配给正确的中间值 我当前的方法是从 C 读取文本文件 并为我读取的每一行更新表中的一行 必须有更快的方法来做
  • 如何使用 Mongodb C# 驱动程序连接多个集合

    我需要将 3 个集合与多个集合合并在一起 lookup我在 C 驱动程序中尝试过 它允许我 lookup用户采集但无法执行秒 lookup用于设置集合 有人可以帮忙吗 db Transactions aggregate lookup fro
  • 将 log4net 与 Autofac 结合使用

    我正在尝试将 log4net 与 Autofac 一起使用 我粘贴了这段代码http autofac readthedocs org en latest examples log4net html http autofac readthed
  • HttpWebRequest 在第二次调用时超时

    为什么以下代码在第二次 及后续 运行时超时 代码挂在 using Stream objStream request GetResponse GetResponseStream 然后引发 WebException 表示请求已超时 我已经尝试过
  • .NET中的LinkedList是循环链表吗?

    我需要一个循环链表 所以我想知道是否LinkedList是循环链表吗 每当您想要移动列表中的 下一个 块时 以循环方式使用它的快速解决方案 current current Next current List First 电流在哪里Linke
  • 为什么在setsid()之前fork()

    Why fork before setsid 守护进程 基本上 如果我想将一个进程与其控制终端分离并使其成为进程组领导者 我使用setsid 之前没有分叉就这样做是行不通的 Why 首先 setsid 将使您的进程成为进程组的领导者 但它也
  • 使用 GhostScript.NET 打印 PDF DPI 打印问题

    我在用GhostScript NET http ghostscriptnet codeplex com打印 PDF 当我以 96DPI 打印时 PDF 打印效果很好 但有点模糊 如果我尝试以 600DPI 打印文档 打印的页面会被极大地放大
  • 当另一个线程可能设置共享布尔标志(最多一次)时,是否可以读取共享布尔标志而不锁定它?

    我希望我的线程能够更优雅地关闭 因此我尝试实现一个简单的信号机制 我不认为我想要一个完全事件驱动的线程 所以我有一个工作人员有一种方法可以使用关键部分优雅地停止它Monitor 相当于C lock我相信 绘图线程 h class Drawi
  • 防止在工厂方法之外实例化对象

    假设我有一个带有工厂方法的类 class A public static A newA Some code logging return new A 是否可以使用 a 来阻止此类对象的实例化new 那么工厂方法是创建对象实例的唯一方法吗 当
  • 在客户端系统中安装后桌面应用程序无法打开

    我目前正在使用 Visual Studio 2017 和 4 6 1 net 框架 我为桌面应用程序创建了安装文件 安装程序在我的系统中完美安装并运行 问题是安装程序在其他计算机上成功安装 但应用程序无法打开 edit 在客户端系统中下载了

随机推荐