Являются ли реализации виртуального наследования C ++ различных компиляторов несовместимыми? - PullRequest
5 голосов
/ 24 июня 2009

У меня есть иерархия открытых интерфейсов, как это:

struct ISwitchable {
    /* Obtain pointer to another implemented interface of the same instance. */
    virtual int switch(unsigned int interfaceId, void** pInstance) = 0;
};
struct IFoo : public ISwitchable { /* Methods */ };
struct IBar : public ISwitchable { /* Methods */ };
struct IFooBar : public IFoo, public IBar { /* Methods */ };

Класс, реализующий IFooBar, помещается в dll вместе с заводской функцией. Клиентский код загружает dll, использует фабричную функцию для создания экземпляра класса и использования его в соответствии с интерфейсами (они поставляются в виде заголовочного файла).

Схема отлично работает с DLL, созданной MSVC, и клиентским кодом, созданным Borland C ++ Builder 6.

Я ввожу виртуальное наследование в иерархию:

struct IFoo : public virtual ISwitchable { /* Methods */ };
struct IBar : public virtual ISwitchable { /* Methods */ };

И когда в той же ситуации (dll от MSVC, клиент от Builder) клиентский код запрашивает экземпляр класса, он получает его с грязным vtable.

Есть ли какое-либо решение, кроме возврата к обычному наследованию?

Ответы [ 4 ]

8 голосов
/ 24 июня 2009

Я не думал, что вы могли бы рассчитывать на то, что любые встроенные классы будут совместимы между компиляторами. Заявляет ли Borland, что они могут загружать и взаимодействовать с классами, созданными MSVC. Если так, похоже, у них есть ошибка. Насколько я знаю, в спецификации C ++ ничего не говорится о точной структуре VTable, поэтому она не будет работать на разных компиляторах.

7 голосов
/ 24 июня 2009

В отличие от C, кросс-компилятор ABI для C ++ отсутствует - компиляторы могут осуществлять виртуальное наследование (и даже обычное наследование) любым удобным для них способом.

В результате: вызов функций C ++ через компиляторы не гарантированно работает. Я знаю, что это уродливо, но если вы хотите, чтобы ваша DLL успешно взаимодействовала с несколькими компиляторами, вам, вероятно, лучше вместо этого предоставить набор простых extern "C" функций и вручную составленные таблицы указателей функций.

Примечание. Компиляторы, которые поддерживают создание объектов COM (или имеют возможность сделать это), более ограничены в своих макетах объектов. (Я знаю, что в последних версиях MSVC ++ создаются COM-совместимые объекты, по крайней мере, в большинстве случаев, хотя я не уверен, что виртуальное наследование включено.)

0 голосов
/ 24 июня 2009

Я подозреваю аргумент void**. Использование указателей void теряет информацию о типе.

Если вы работаете с множественным наследованием, то информация о типе может иметь важное значение. Рассмотрим:

class Foo { ... };
class Bar { ... };

class Both: public Foo, public Bar { ... };

Предположим, что внутренне макет экземпляра Both является экземпляром Foo, за которым следует экземпляр Bar. Я могу передать Both * методу, ожидающему Foo * без проблем. Я также могу передать Both * методу, ожидающему Bar *, при условии, что я настрою указатель так, чтобы он указывал на встроенный Bar. Я могу сделать это надежно, потому что знаю, что работаю с обоими.

Сейчас:

Foo *foo = new Both(...);
Bar *bar = new Both(...);

void *p = foo;
void *q = bar;

Both *both = (which) ? (Both*)p : (Both*)q;

Итак: как мне узнать, как настроить p или q, когда я назначаю «обоим»? Я не могу, потому что информация о типе теряется через указатель void.

Вариант этой проблемы может быть связан с проблемами, которые у вас есть.

0 голосов
/ 24 июня 2009

Наверное, не ответит на ваш вопрос ... Но настоятельно рекомендуется НЕ использовать множественное наследование, как вы (это называется «страшный бриллиант»).

...