передача структур C ++ без vtables через границу DLL?

Передача классов через границы 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 с другой версией компилятора и т. Д.?

1

Решение

Ваш код действительно эквивалентен следующему коду 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.

2

Другие решения

Ни границы DLL, ни vtables никоим образом не являются особенными. Границы раздела выпуска

Если вы смешиваете объекты, скомпилированные с разными компиляторами, разными версиями компилятора или иногда разными параметрами компилятора, в одном исполняемом файле, есть реальная возможность получить несовместимые макеты объектов в разных частях программы. Это может произойти, если вы используете DLL или статические ссылки, и с любым видом объектов, vtables или нет.

Так что, если ваш исполняемый файл и все его библиотеки DLL скомпилированы и выпущены вместе в целом, вам не о чем беспокоиться. Если исполняемый файл и библиотеки DLL являются разными продуктами, выпускаемыми отдельно независимыми организациями, вам следует быть очень осторожным. Целесообразно использовать C-совместимые структуры данных только через границы модуля выпуска и никогда не выделять свободную память, выделенную другому модулю.

1