Совместимость ABI интерфейсов (абстрактных классов) с другими виртуальными изменениями - PullRequest
3 голосов
/ 13 сентября 2011

Остается ли стабильным ABI представления класса, даже если в производный класс внесены другие изменения, включая виртуальные?

То есть, скажем, у меня есть интерфейс InterfaceA (абстрактный класс смного чистых виртуальных функций) и класс DerivedB, который наследуется от него.Я пишу библиотеку, которая имеет функцию, принимающую InterfaceA *.Что я хочу знать, так это то, остается ли интерфейс двоичным совместимым, пока сам интерфейс не меняется.

Очистите, если я изменю InterfaceA, я не ожидаю, что код будет двоичным совместимым.Однако, что если я просто изменю DerivedB, скажем, я наследую больше интерфейсов или добавлю другие виртуальные функции.В крайнем случае я умножаю наследовать от другого класса, который определяет InterfaceA.Остаётся ли InterfaceA бинарно-совместимым, несмотря на все эти изменения?

Мое предположение и опыт на данный момент таковы, что да, он совместим.Я просто жду подтверждения этого (или опровержения, если оно несовместимо).

Примечание: меня не волнует динамическая типизация или другие типы преобразования, меня интересуют только сами функции интерфейса.

Также обратите внимание: предположим, что используемые версии компилятора стабильны в целом как ABI - без существенных изменений версии.

Ответы [ 2 ]

3 голосов
/ 13 сентября 2011

Да, пока имя, аргументы и порядок виртуальных функций в InterfaceA не изменятся, он останется двоично-совместимым. Обратите внимание, что это позволяет добавлять функции в конце объявления класса.

(Возможно, это явно не гарантируется спецификацией C ++, но COM полагается на это, поэтому большие компиляторы C ++ будут работать таким образом.)

0 голосов
/ 18 декабря 2014

Предполагая, что вы не используете DerivedB через границу ABI, вы должны иметь возможность делать с ней все, что захотите. Чистый виртуальный класс (DerivedA) - это то, что важнее всего, и если вы не меняете его, то вы правы - все, что использует этот указатель на InterfaceA, не будет иметь проблем с пересечением границы.

Фактически, вы даже можете добавить функцию в конец InterfaceA, если это конечный интерфейс (т.е. никакие другие интерфейсы не наследуются от него) и функция не является перегрузкой другой функции. Конечно, он должен следовать тем же «правилам ABI», что и другие ваши функции - т.е. типы параметров должны быть примитивными типами или указателями на другие интерфейсы и т. Д. Если у вас есть система управления версиями, ваше приложение может проверить версию плагина и определить, или нет, новая функция может быть безопасно вызвана - таким образом, добавляются функциональные возможности для новых плагинов, но старые плагины, скомпилированные до того, как изменения будут работать. Довольно круто!

...