Hay muchos problemas de portabilidad con C ++, que se debe solo a la falta de estandarización a nivel binario.
No creo que sea tan simple. Las respuestas proporcionadas ya proporcionan una excelente justificación en cuanto a la falta de enfoque en la estandarización, pero C ++ puede ser un lenguaje demasiado rico para ser adecuado para competir genuinamente con C como estándar ABI.
Podemos entrar en el cambio de nombre como resultado de la sobrecarga de funciones, incompatibilidades de vtable, incompatibilidades con excepciones a través de los límites del módulo, etc. Todo esto es un verdadero problema, y desearía que al menos pudieran estandarizar los diseños de vtable.
Pero un estándar ABI no se trata solo de hacer discos duros C ++ producidos en un compilador capaces de ser utilizados por otro binario construido por un compilador diferente. ABI se usa en varios idiomas . Sería bueno si al menos pudieran cubrir la primera parte, pero no hay forma de que vea que C ++ compita realmente con C en el tipo de nivel ABI universal tan crucial para hacer los dibes más compatibles.
Imagine un simple par de funciones exportadas de esta manera:
void f(Foo foo);
void f(Bar bar, int val);
... e imagina Foo
y Bar
fueron clases con constructores parametrizados, constructores de copia, constructores de movimiento y destructores no triviales.
Luego tome el escenario de Python / Lua / C # / Java / Haskell / etc. desarrollador tratando de importar este módulo y usarlo en su idioma.
Primero, necesitaríamos un estándar de cambio de nombre sobre cómo exportar símbolos utilizando la sobrecarga de funciones. Esta es una parte más fácil. Sin embargo, no debería ser realmente el nombre "destrozar". Dado que los usuarios de dylib tienen que buscar símbolos por nombre, las sobrecargas aquí deberían conducir a nombres que no parecen un desastre completo. Tal vez los nombres de los símbolos podrían ser "f_Foo"
"f_Bar_int"
o algo por el estilo. Tendríamos que asegurarnos de que no puedan entrar en conflicto con un nombre realmente definido por el desarrollador, tal vez reservando algunos símbolos / caracteres / convenciones para el uso de ABI.
Pero ahora es un escenario más difícil. ¿Cómo el desarrollador de Python, por ejemplo, invoca constructores de movimientos, constructores de copias y destructores? Tal vez podríamos exportarlos como parte del dylib. Pero, ¿ Foo
y si Bar
se exportan en diferentes módulos? ¿Deberíamos duplicar los símbolos y las implementaciones asociadas en este dylib o no? Sugeriría que lo hagamos, ya que podría ser realmente molesto muy rápido, de lo contrario, comenzar a enredarse en múltiples interfaces dylib solo para crear un objeto aquí, pasarlo aquí, copiar uno allí, destruirlo aquí. Si bien la misma preocupación básica podría aplicarse de alguna manera en C (solo de forma más manual / explícita), C tiende a evitar esto solo por la forma en que las personas programan con él.
Esta es solo una pequeña muestra de la incomodidad. ¿Qué sucede cuando una de las f
funciones anteriores arroja unBazException
(también una clase C ++ con constructores y destructores y derivando std :: excepción) en JavaScript?
En el mejor de los casos, creo que solo podemos esperar estandarizar un ABI que funcione desde un binario producido por un compilador de C ++ a otro binario producido por otro. Eso sería genial, por supuesto, pero solo quería señalar esto. Por lo general, acompañar tales inquietudes para distribuir una biblioteca generalizada que funcione con compiladores cruzados también suele ser el deseo de que sea un lenguaje cruzado realmente generalizado y compatible.
Solución sugerida
Mi solución sugerida después de luchar por encontrar formas de usar interfaces C ++ para API / ABI durante años con interfaces de estilo COM es convertirme en un desarrollador "C / C ++" (juego de palabras).
Use C para crear esas ABI universales, con C ++ para la implementación. Todavía podemos hacer cosas como exportar funciones que devuelven punteros a clases opacas de C ++ con funciones explícitas para crear y destruir tales objetos en el montón. Intente enamorarse de esa estética C desde una perspectiva ABI, incluso si estamos usando totalmente C ++ para la implementación. Las interfaces abstractas pueden modelarse utilizando tablas de punteros de función. Es tedioso incluir todo esto en una API C, pero los beneficios y la compatibilidad de la distribución que viene con eso tenderán a hacer que valga la pena.
Entonces, si no nos gusta usar esta interfaz directamente (probablemente no deberíamos hacerlo al menos por razones RAII), podemos envolver todo lo que queramos en una biblioteca C ++ estáticamente vinculada que enviamos con el SDK. Los clientes de C ++ pueden usar eso.
Los clientes de Python no querrán usar una interfaz C o C ++ directamente, ya que no hay formas de hacer esas pythonique. Querrán envolverlo en sus propias interfaces de Pythonique, por lo que en realidad es bueno que solo estemos exportando un mínimo de C API / ABI para que sea lo más fácil posible.
Creo que gran parte de la industria de C ++ se beneficiaría de hacer esto más que tratar de enviar tercamente interfaces de estilo COM, etc. También facilitaría todas nuestras vidas como usuarios de estos dylibs para no tener que preocuparse por las incómodas ABI. C lo hace simple, y su simplicidad desde una perspectiva ABI nos permite crear API / ABI que funcionan de forma natural y con minimalismo para todo tipo de FFI.