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