Cómo diseñar una API de C++ para extensibilidad compatible con binarios


Estoy diseñando una API para una biblioteca de C++ que se distribuirá en un objeto dll / shared. La biblioteca contiene clases polymorhic con funciones virtuales. Me preocupa que si expongo estas funciones virtuales en la API DLL, me corte de la posibilidad de extender las mismas clases con más funciones virtuales sin romper la compatibilidad binaria con las aplicaciones construidas para la versión anterior de la biblioteca.

Una opción sería usar el modismo PImpl para ocultar todas las clases que tienen funciones virtuales, pero que también parecen tener sus limitaciones: de esta manera las aplicaciones pierden la posibilidad de subclasificar las clases de la biblioteca y sobreescribir los métodos virtuales.

¿Cómo diseñaría una clase API que puede ser subclasificada en una aplicación, sin perder la posibilidad de extender la API con métodos virtuales (no abstractos) en una nueva versión de la dll mientras se mantiene compatible con binarios anteriores?

Actualización: las plataformas de destino para las bibliotecas son windows / msvc y linux / gcc.

Author: xpda, 2009-11-21

5 answers

Hace varios meses escribí un artículo llamado "Compatibilidad Binaria de Bibliotecas Compartidas Implementadas en C++ en Sistemas GNU/Linux" [pdf]. Si bien los conceptos son similares en el sistema Windows, estoy seguro de que no son exactamente los mismos. Pero después de leer el artículo se puede obtener una idea de lo que está pasando en el nivel binario de C++ que tiene algo que ver con la compatibilidad.

Por cierto, la interfaz binaria de la aplicación GCC se resume en un borrador de documento estándar " Itanium ABI ", por lo que tendrá una base formal para un estándar de codificación que elija.

Solo para un ejemplo rápido: en GCC puede extender una clase con más funciones virtuales, si ninguna otra clase la hereda. Lea el artículo para un mejor conjunto de reglas.

Pero de todos modos, las reglas a veces son demasiado complejas para entenderlas. Por lo tanto, podría estar interesado en una herramienta que verifique la compatibilidad de dos versiones dadas: abi-compliance-checker para Linux.

 29
Author: P Shved,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-17 21:04:10

Hay un interesante artículo sobre la base de conocimiento de KDE que describe lo que se debe y lo que no se debe hacer cuando se apunta a la compatibilidad binaria al escribir una biblioteca: Políticas / Problemas de compatibilidad Binaria Con C++

 11
Author: Gregory Pakosz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-20 08:56:44

Compat binario C++ es generalmente difícil, incluso sin herencia. Mira GCC por ejemplo. En los últimos 10 años, no estoy seguro de cuántos cambios de breaking ABI han tenido. Entonces MSVC tiene un conjunto diferente de convenciones, por lo que no se puede vincular a eso con GCC y viceversa... Si comparas esto con el mundo C, el compilador inter-op parece un poco mejor allí.

Si estás en Windows deberías mirar COM. A medida que introduce nuevas funcionalidades, puede agregar interfaces. A continuación, las personas que llaman puede QueryInterface() para que el nuevo exponga esa nueva funcionalidad, e incluso si termina cambiando las cosas mucho, puede dejar la implementación antigua allí o puede escribir shims para las interfaces antiguas.

 1
Author: asveikau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-21 08:56:00

Creo que malinterpretas el problema de la subclase.

Aquí está tu Pimpl:

// .h
class Derived
{
public:
  virtual void test1();
  virtual void test2();
private;
  Impl* m_impl;
};

// .cpp
struct Impl: public Base
{
  virtual void test1(); // override Base::test1()
  virtual void test2(); // override Base::test2()

  // data members
};

void Derived::test1() { m_impl->test1(); }
void Derived::test2() { m_impl->test2(); }

¿Ves ? No hay problema con sobreescribir los métodos virtuales de Base, solo necesita asegurarse de volver a declararlos virtual en Derived para que aquellos que derivan de Derivados sepan que también pueden reescribirlos (solo si lo desea, que por cierto es una gran manera de proporcionar un final para aquellos que lo carecen), y aún puede redefinirlo por sí mismo en Impl que incluso puede llamar a la Base versión.

No Hay ningún problema con Pimpl allí.

Por otro lado, se pierde polimorfismo, que puede ser problemático. Depende de usted decidir si desea polimorfismo o simplemente composición.

 1
Author: Matthieu M.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-11 18:53:17

Si expone la clase PImpl en un archivo de encabezado, entonces puede heredar de él. Aún puede mantener la portabilidad hacia atrás ya que las clases externas contienen un puntero al objeto PImpl. Por supuesto, si el código cliente de la biblioteca no es muy sabio, podría usar mal este objeto PImpl expuesto, y arruinar la compatibilidad binaria con versiones anteriores. Puede agregar algunas notas para advertir al usuario en el archivo de encabezado de PImpl.

 0
Author: Daniel Heilper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-23 09:01:08