У меня есть некоторые проблемы с безопасностью преобразований типов. Я разрабатываю абстрактный интерфейс, ведь это будет поддерживаться плагинами, экспортирующими объект, ориентированный на объект C ABI , то есть указатели на объекты и функции стиля С формы func(void *this, ...)
, а не на функции элемента стиля C++, они затем будут упакованы в структуру, представляющую реализацию объектов. Однако некоторые из моих базовых фреймворков используют несколько виртуальных наследований.Пропустить объект C++ (с возможным множественным виртуальным наследованием) через C ABI через указатель void
Упрощенный пример
class A
{
public:
virtual void doA()
}
class B
{
public:
virtual void doB()
}
class C : public A, public B
{
public:
virtual void doA()
virtual void doB()
}
struct impA
{
(*doA)(void *self);
}
struct impB
{
(*doB)(void *self);
}
struct impC
{
(*doA)(void *self);
(*doB)(void *self);
}
void * AfromC(void *v) {
C*c = reinterpret_cast<C*>(v); // Known to be C* type
return static_cast<void*>(static_cast<A*>(c)); // method 1
return reinterpret_cast<void*>(static_cast<A*>(c)); // method 2
//method 3 & 4
C** c = static_cast<C**>(v); // Known to be C* type
return static_cast<void*>(&static_cast<A*>(*c)); // method 3
return static_cast<void*>(static_cast<A**>(c)); // method 4
}
/////////// main code
class A
{
public:
void doA() { imp.doA(self); }
private:
impA imp;
void *self;
}
class B
{
public:
void doB() { imp.doB(self); }
private:
impB imp;
void *self;
}
Рассмотрим AfromC, у меня есть 4 возможных методов получения указателя, который я могу спокойно пройти через C ABI, я хочу известно рассмотрение этих различных методов, мое предпочтение будет метод 1.
Я не уверен, что все эти методы являются законными или безопасными.
Примечание: Объект всегда будет доступ функций в двоичном, из которого они созданы/уничтожены они возвращаются/принимать другие объекты, обрабатываемые ими типы самостоятельно или C-типа данных (до структур из СОД)
Хотя я нашел упоминание о таких вещах в сети, все они касаются людей, имеющих проблемы в результате преобразования в void, т. Е. A* a= static_cast<A*>(static_cast<void*>(c)) // c -> C*
, что следует ожидать, поскольку это не исправляет vtable, и решениями является использование абстрактного базовый тип (этот wron't работает для меня, поскольку мне нужно пройти через C ABI), однако я также слышал упоминание о том, что виртуальные указатели больше обычных указателей, поэтому моя причина для рассмотрения методов 3 и 4, поскольку это было бы нормальный указатель на более крупный указатель и, таким образом, безопасный даже для типов с большими указателями.
Так что мой главный вопрос - это метод 1 работать с проблемой? Aslo я мог бы безопасно определить функцию шаблона по линиям template <typename T, typename U> void * void_cast(U v) { static_cast<void *>(static_cast<T>(v)); }
, чтобы упростить код плагина. Наконец, если метод 1 является правильным, почему? и может ли использоваться какой-либо из методов?
Можете ли вы уточнить «но va == vc не гарантируется», учитывая, что я передал va в a как A * = static_cast (va), могу ли я затем передать это объекту типа C, то есть либо static_cast (a), либо reinterpret_cast (a), учитывая, что я знаю, что va был преобразован как в y наш ответ от объекта типа C, или это не будет более безопасным после литья через тип пустоты? –
glenflet
«разыменование a2 - неопределенное поведение», чтобы объяснить, почему вы так думаете? Из того, что я могу сказать, в общем представлении 'a2' и' c2' производятся тождественно из 'a' и' c', поэтому они должны быть одинаково UB - либо да, либо нет, а не наполовину. – dascandy
@glenflet: Никакой гарантии для этого не может быть найден в стандарте. А в реализациях, использующих vtables для виртуальных функций, распространено, что адрес объекта является адресом vtable. В этом случае static_cast выполняет коррекцию смещения, но фактическое значение 'va' и' vc' отличается в этом случае. Просто попробуйте с отладчиком и базовыми и подклассами, имеющими виртуальные функции. –