Поскольку мы не должны передавать ничего, кроме простой старой структуры данных [1], через границу плагина, я пришел к следующей идее, чтобы передать объект:
Мой вопрос: Есть лучший способ сделать это ?
[РЕДАКТИРОВАТЬ] См. Мое редактирование ниже с, вероятно, лучшим решением с использованием стандартного макета объекта.
Вот игрушечный пример, иллюстрирующий идею:
Я хочу передать 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 ++ не гарантирует какой-либо двоичной совместимости между различными компиляторами.
Не существует всеобъемлющего независимого от реализации правила о расположении членов класса, которое могло бы обеспечить это (см. Также этот так ответ
на относительный адрес членов данных).
Конечно, к счастью, на практике многие разные компиляторы используют одну и ту же логику в стандартных объектах компоновки для заполнения и выравнивания, используя
конкретные рекомендации или требования к архитектуре процессора (при условии, что не используется пакет или переключатель компоновки с экзотическим выравниванием). Поэтому использование POD / стандартная верстка относительно безопасен (и как якк
правильно указал: «Если вы доверяете моду, вы должны доверять стандартному макету».)
Так что ваш код может работать. Другие альтернативы, полагающиеся на виртуалы c ++ во избежание проблем с именами, также работают кросс-компилятором
как объяснено в Эта статья. По той же причине: на практике многие компиляторы используют в одной конкретной архитектуре OS + признанный подход
для построения своих таблиц. Но опять же, это наблюдение из практики и не абсолютная гарантия.
Если вы хотите дать кросс-компиляльное соответствие гарантия для вашей библиотеки, то вы должны полагаться только на настоящие гарантии, а не только обычные
практика. На MS-Windows двоичный интерфейс стандарт для объектов COM. Вот это всеобъемлющий C ++ COM учебник. Это может
быть немного старым, но ни у кого другого нет такого количества иллюстраций, чтобы сделать его понятным.
Подход COM, конечно, тяжелее, чем ваш фрагмент. Но это стоимость кросс-компилятора и даже гарантии соответствия между языками, которую он предлагает.