c ++ plugin:跨对象跨模拟对象 [英] c++ plugin : pass object across boundary (emulating it)

查看:199
本文介绍了c ++ plugin:跨对象跨模拟对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因为我们不应该通过插件边界中的任何东西而不是普通数据结构[1],我想出了以下想法,以便传递一个对象:




  • 暴露插件C接口中的所有公共方法,在应用程序端,将插件包装在对象中。 (请参阅以下示例)



我的问题是:有更好的方法吗?
请参阅下面的编辑,并使用标准布局对象更好的解决方案。



想法:



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

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

但是,我们知道由于兼容性问题,不应该直接进行。
这个想法是将Writer的界面暴露给插件中的自由函数:

  // plugin 

externC{
Writer * create_writer(){
返回新的PluginWriterImpl {};
}

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

void delete_writer(Writer * this _){
delete This_;
}
}

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

  // app 

类WriterWrapper:public Writer {
private:
Writer * the_plugin_writer; //对象被包装
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 ++ plugin:是否可以传递多态对象?






跨越边界的标准布局。如果是,这样的解决方案是否正确? (可以简化吗?)



我们要跨越边界传递Writer:

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

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

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

-

  // plugin_impl.cpp 

#includeplugin.h

externC{
PluginWriter * create_writer();
void delete_writer(PluginWriter * pw);
}

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

-

  // app 
#includeplugin.h

class WriterWrapper:public Writer {
private:
PluginWriter * the_plugin_writer ; //对象被包装
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插件。#include插件。 h

解决方案

在客户端和库端使用不同编译器(甚至语言)的DLL需要二进制兼容性href =http://en.wikipedia.org/wiki/Application_binary_interface =nofollow> ABI )。



无论是标准布局或POD,C ++标准不保证不同编译器之间的任何二进制可交换性。
对于可以确保这一点的类成员的布局,没有全面的实现独立规则(另见此SO回答
在数据成员的相对地址上)。当然,幸运的是,在实践中 许多不同的编译器在标准布局对象中使用相同的逻辑来填充和对齐,使用
CPU架构的特定最佳实践或要求(只要没有使用打包或异常对齐编译器开关)。因此,使用POD /标准布局相对安全(正如Yakk
正确指出的:如果您信任pod,您必须信任标准布局。 )



所以你的代码可能工作。其他替代方法,依赖于c ++虚拟以避免名称错误问题,似乎工作交叉编译器
,如本文。同样的原因:在实践中,许多编译器在一个特定的OS +架构上使用一个公认的方法
来构造它们的vtable。但同样,这是从实践和不是绝对的保证的观察。



如果您想为图书馆提供一个交叉的类似符合保证,那么您应该只依赖真正的gurantees,而不仅仅是通常的
练习。在MS-Windows上,对象的二进制界面标准 COM 。以下是一个全面的 C ++ COM教程。它可能
有点老了,但没有其他有这么多插图,使其可以理解。



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


Since we shouldn't pass anything else than Plain Old Data-structure[1] across a plug-in boundary, I came up with to following idea in order to pass an object :

  • expose all the public method in the plugin "C" interface, and on the application side, wrap the plugin in an object. (See the following example)

My question is : Is there a better way to do this ? [EDIT] See my edit below with a probably better solution using standard-layout object.

Here is a toy example illustrating the idea :

I want to pass a Writer across the boundary :

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

However, we know that it shouldn't be done directly because of compatibility issue. The idea is to expose the Writer's interface as free functions in the plugin :

// 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_;
   }
}

and to wrap all those function call in a wrapper object on the application side :

// 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);
      }
};

This leads to lots of forwarding function. Nothing else than POD cross the boundary, and the application doesn't know about the fact that the current Writer's implementation comes from a plugin.

[1] For binary compatibility issues. For more information, you can see this related SO question : c++ plugin : Is it ok to pass polymorphic objects?


[EDIT] It seems that we could pass standard-layout across the boundary. If so, would such a solution be correct ? (And could it be simplified ?)

We want to pass a Writer across the boundary :

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

So we will pass a standard-layout object form the plugin to the app, and wrap it on the application side.

// 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);
      }
};

However, I fear that the linker will complain while compiling the app because of the : #include plugin.h

解决方案

Using a DLL with different compilers (or even languages) on client and on library side requires binary compatiblity (aka ABI).

Whatever is said about standard layout or POD, the C++ standard does not guarantee any binary commpatibility between different compilers. There is no comprehensive implementation independent rule on the layout of class members that could ensure this (see also this SO answer on relative address of data members ).

Of course, fortunately, in practice many different compilers use the same logic in standard layout objects for padding and aligning, using the specific best practices or requirements for CPU architecture (as long as no packing or exotic alignment compiler switch is used). Therefore the use of POD/standard layout is relatively safe (and as Yakk correctly pointed out: "if you trust pod, you must trust standard layout.")

So your code may work. Other alternatives, relying on c++ virtuals to avoid name mangling issues, seem to work cross compiler as well as explained in this article. For the same reason: in practice many compilers use on one specific OS+architecture a recognized approach for constructing their vtables. But again, that's an observation from practice and not an absolute guarantee.

If you want to give a cross-compilar conformity guarantee for your library, then you should rely only on real gurantees and not only usual practice. On MS-Windows, the binary interface standard for objects is COM. Here is a comprenhensive C++ COM tutorial. It might be a little bit old, but no other has so many illustrations to make it understandable.

COM approach is of course heavier than your snippet. But that's the cost of the cross compiler and even cross language compliance garantee it offers.

这篇关于c ++ plugin:跨对象跨模拟对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆