Передача классов через границы DLL — плохая идея из-за неопределенного размещения vtable в c ++,
но что, если я явно установлю соглашение о вызовах и избежу виртуальных функций и наследования?
Другими словами, могу ли я безопасно передать указатели на следующую структуру через DLL?
struct MyStruct {
int a;
int b;
WINAPI MyStruct(int a, int b)
: a(a), b(b)
{}
void WINAPI SetA(int a) {
this->a = a;
}
};
Было бы безопасно использовать ссылку на такую DLL с другой версией компилятора и т. Д.?
Ваш код действительно эквивалентен следующему коду C:
struct MyStruct {
int a;
int b;
};
void WINAPI InitMyStruct(struct MyStruct* p, int a, int b)
{
p->a = a; p->b = b;
}
void WINAPI MyStruct_SetA(struct MyStruct* p, int a)
{
p->a = a;
}
Отказ от виртуальных функций практически ничего не принес; все равно это зависит от компилятора, как будут называться эти «эквивалентные» функции C («искажение имени»), поэтому вам нужно использовать совместимые компиляторы. Все версии MSVC этого тысячелетия совместимы друг с другом в этом отношении. Все версии GCC этого тысячелетия совместимы друг с другом. Просто не смешивайте два (ошибки во время соединения будут происходить).
Есть и другие источники проблем:
Убедитесь, что ваши настройки упаковки / выравнивания совпадают (но они должны делать это и для простых интерфейсов Си).
Если вы используете «new» в одной DLL и «delete» в другой, у вас могут возникнуть проблемы, если вы не используете точно такую же версию компилятора и не используете библиотеку времени выполнения DLL. Так что не new
или же delete
ваш объект MyStruct из клиентского кода; вместо этого предоставьте функции внутри DLL, чтобы сделать это за вас.
Держитесь подальше от стандартных контейнеров библиотеки в вашем интерфейсе. Они не будут работать, если DLL и клиент не связаны с одной и той же стандартной библиотекой.
Не бойтесь виртуальных функций.
ЗаметкаВсе эти проблемы теоретически существуют и на других платформах, но на практике кажутся немного менее актуальными для Linux и Mac OS X.
Ни границы DLL, ни vtables никоим образом не являются особенными. Границы раздела выпуска
Если вы смешиваете объекты, скомпилированные с разными компиляторами, разными версиями компилятора или иногда разными параметрами компилятора, в одном исполняемом файле, есть реальная возможность получить несовместимые макеты объектов в разных частях программы. Это может произойти, если вы используете DLL или статические ссылки, и с любым видом объектов, vtables или нет.
Так что, если ваш исполняемый файл и все его библиотеки DLL скомпилированы и выпущены вместе в целом, вам не о чем беспокоиться. Если исполняемый файл и библиотеки DLL являются разными продуктами, выпускаемыми отдельно независимыми организациями, вам следует быть очень осторожным. Целесообразно использовать C-совместимые структуры данных только через границы модуля выпуска и никогда не выделять свободную память, выделенную другому модулю.