¿Por qué C proporciona 'enlaces' de lenguaje donde C ++ se queda corto?

60

Recientemente me preguntaba cuándo usar C sobre C ++, y viceversa. Afortunadamente, alguien ya me ganó y, aunque me llevó un tiempo, pude digerir todas las respuestas y comentarios a esa pregunta.

Sin embargo, un elemento en esa publicación sigue siendo abordado una y otra vez, sin ningún tipo de ejemplo, verificación o explicación:

"El código C es bueno para cuando quieres tener enlaces de múltiples idiomas para tu biblioteca"

Eso es una paráfrasis. Debo señalar que varias personas señalan que los enlaces de múltiples idiomas son posibles en C ++ (a través de algún externfuncionamiento), pero sin embargo, si lees esa publicación en su totalidad, es bastante obvio que C es ideal para la portabilidad / enlace de lenguaje. Mi pregunta es: ¿por qué?

¿Alguien puede proporcionar razones concretas por las que escribir bibliotecas en C permite enlaces y / o portabilidad más fáciles en otros idiomas?

smeeb
fuente
66
Principalmente por razones históricas y sociales. La mayoría de las implementaciones de lenguaje y los sistemas de tiempo de ejecución están construidos por encima de C. Por ejemplo, Ocaml, SBCL, los tiempos de ejecución de Haskell están codificados en C (y no ganarán mucho al ser recodificados en C ++)
Basile Starynkevitch
8
En la práctica, pegar bibliotecas C ++ genuinas (piense en Qt) en estos tiempos de ejecución es doloroso.
Basile Starynkevitch
3
@Basile: Qt no es una biblioteca genuina de C ++. Además, incluso para bibliotecas más dolorosas, solo es doloroso porque expresan semánticas realmente útiles.
DeadMG
2
Lea Essential COM de Don Box. Explica el proceso de crear un ABI en C ++. COM fue la forma en que Microsoft creó un ABI. es decir, las bibliotecas COM C ++ podrían usarse desde C o VB u otros lenguajes.
user93353

Respuestas:

69

C tiene una interfaz mucho, mucho más simple, y sus reglas para convertir una interfaz de código fuente en una interfaz binaria son tan sencillas que la generación de interfaces externas para enlazar se realiza de una manera bien establecida. C ++, por otro lado, tiene una interfaz increíblemente complicada, y las reglas para el enlace ABI no están estandarizadas en absoluto, ni formalmente ni en la práctica. Esto significa que casi cualquier compilador para cualquier lenguaje para cualquier plataforma puede unirse a una interfaz C externa y saber exactamente qué esperar, pero para una interfaz C ++, es esencialmente imposible porque las reglas cambian dependiendo de qué compilador, qué versión y qué plataforma con la que se creó el código C ++.

En C, tampoco hay reglas estándar de implementación de lenguaje binario, pero es un orden de magnitud más simple y en la práctica los compiladores usan las mismas reglas. Otra razón por la que el código C ++ es difícil de depurar es la gramática complicada mencionada anteriormente, ya que los depuradores con frecuencia no pueden manejar muchas características del lenguaje (colocar puntos de interrupción en plantillas, analizar comandos de conversión de puntero en ventanas de visualización de datos, etc.).

La falta de un ABI estándar (interfaz binaria de aplicación) tiene otra consecuencia: hace que el envío de interfaces C ++ a otros equipos / clientes sea poco práctico, ya que el código de usuario no funcionará a menos que se compile con las mismas herramientas y opciones de compilación. Ya hemos visto otra fuente de este problema: la inestabilidad de las interfaces binarias debido a la falta de encapsulación en tiempo de compilación.

- C ++ defectuoso

Mason Wheeler
fuente
44
Debe tener en cuenta que, si bien es posible definir una API similar a C implementada en C ++ ( extern "C", aún es más trabajo adicional hacerlo, que debe equilibrarse con las características proporcionadas por C ++)
jhominal
55
El ABI de C ++ es irrelevante en el contexto de la pregunta, donde las bibliotecas de C ++ se pueden exportar fácilmente extern "C".
DeadMG
12
@ jhominal: es mucho mejor que escribirlo en C, donde debes definir una interfaz C y luego también debes implementarla en C, mientras que en C ++ puedes definir una interfaz C y luego no tienes que implementar en C. No importa en qué lenguaje esté implementando, todavía tiene que definir una interfaz C; esto es cierto en C ++, ya que está en C o en cualquier otro lenguaje que pueda exponer los enlaces C.
DeadMG
99
¿Sería posible agregar un descargo de responsabilidad al enlace a esa imitación de FQA?
Martin Ba
2
@DocBrown: Claro que me parece que la pregunta es sobre enlaces de lenguaje C frente a enlaces de lenguaje C ++.
Mason Wheeler
32

Si está tratando de comunicarse con un hablante de otro idioma, el pidgin es más fácil que el inglés de Shakespeare.

Los conceptos de C (llamadas de función, punteros, cadenas terminadas en NULL) son muy sencillos, por lo que otros lenguajes pueden implementarlos fácilmente lo suficientemente bien como para llamar a funciones de C. Por razones históricas, se implementan muchos otros lenguajes en C, lo que hace que llamar a las funciones de C sea aún más fácil.

C ++ agrega bastantes cosas: clases, con herencia y vtables y modificadores de acceso; excepciones, con el desbobinado de la pila y el cambio del flujo de control; plantillas. Todo esto dificulta que otros lenguajes usen enlaces de C ++: en el mejor de los casos, hay más "pegamento" o código de interoperabilidad para implementar, y en el peor de los casos, los conceptos no se traducen directamente (debido a diferencias en los modelos de clase, manejo de excepciones, etc.) Para las plantillas en particular, simplemente usarlas (crear instancias) generalmente requiere un paso de compilación con un compilador de C ++, lo que complica enormemente su uso desde otros entornos.

Dicho todo esto, es posible exagerar la dificultad de proporcionar enlaces desde una biblioteca C ++ a otro idioma:

  • Los enlaces de C ++ pueden ser tan compatibles como C, si está dispuesto a trabajar en ello. Como señala @DeadMG, C ++ es compatible extern "C", por lo que puede exportar enlaces de lenguaje de estilo C (con toda la simplicidad y compatibilidad de enlaces de C) desde una biblioteca de C ++ (con la limitación de que no puede exponer ninguna funcionalidad específica de C ++) .
  • Otra objeción común a los enlaces de lenguaje C ++ es la falta de estabilidad ABI para C ++, pero esto también está exagerado; Los ABI de C ++ están menos estandarizados que los ABI de C, pero existen estándares y estándares de facto (el Itanium C ++ ABI, que también se usa en OS X ; el estándar de facto de GCC para Linux). Windows es peor, pero incluso en Windows, permanecer dentro de una versión de Visual C ++ debería funcionar bien.
Josh Kelley
fuente
1
El otro problema al proporcionar un enlace de una biblioteca de C ++ a otro idioma es que el otro idioma puede requerir que se implementen enlaces en C. O, para los idiomas que tienen algo como (.NET) P / Invoke o ctypes (python), Es posible que no proporcione ninguna herramienta para usar el ABI de C ++.
Random832
66
@ Random832: lo cual es completamente irrelevante cuando el lado C ++ simplemente puede ofrecer la interfaz C. No tiene que implementar el enlace en C para ofrecer un enlace en C.
DeadMG
21

C es uno de los idiomas más antiguos que existen. Su ABI es simple, y prácticamente todos los sistemas operativos que todavía se usan hoy en día se han escrito en él . Si bien algunos de esos sistemas operativos pueden haber agregado cosas, por ejemplo, en C # / .NET o lo que sea en la parte superior, a continuación están muy inmersos en C.

Eso significa que, para utilizar la funcionalidad proporcionada por el sistema operativo, prácticamente todos los lenguajes de programación necesitan una forma de interactuar con las bibliotecas C de todos modos . Perl, Java, C ++, todos de forma nativa proporcionan formas de "hablar C", porque tenían que hacerlo si no querían reinventar cada rueda que hay.

Esto hace que C sea el latín de los lenguajes de programación. (¿Cuántos años de internet antes de esa metáfora tiene que ser "el inglés de los lenguajes de programación"?)


Cuando escribe su biblioteca en C, obtiene una interfaz compatible con C de forma gratuita (obviamente). Si está escribiendo su biblioteca en C ++, puede obtener enlaces C, a través de extern "C"declaraciones como usted mencionó.

Sin embargo , se puede conseguir esas fijaciones solamente para la funcionalidad que puede ser expresado en C .

Por lo tanto, su API de biblioteca no puede hacer uso de ...

  • plantillas,
  • clases
  • excepciones,
  • cualquier función que tome o devuelva objetos.

Un ejemplo simple, necesitaría hacer que sus funciones exportadas tomen y devuelvan matrices ( []) en lugar de std::vector(o std::stringpara el caso).

Por lo tanto, no solo no podrá proporcionar ninguna de las cosas buenas que C ++ tiene para ofrecer a los clientes de su biblioteca, sino que también tendrá que hacer un esfuerzo adicional para "traducir" la API de su biblioteca de C ++ a "C compatible" ( extern "C").

Es por eso que se puede señalar que C es la mejor opción para implementar una biblioteca. Personalmente, creo que los beneficios de C ++ aún superan el esfuerzo necesario para una extern "C"API, pero solo soy yo.

DevSolar
fuente
Windows parece estar tratando de basarse en .NET, Android se basa en Java (también C como detalle de implementación para algunas API) e iOS / OSX se basan en Objective-C.
user253751
1
El inglés ya es el idioma dominante en el mundo de la programación. Más dominante que en otras profesiones.
Siyuan Ren
3
@immibis: Windows, Linux / Android y BSD / OSX son todos los núcleos escritos en C, con interfaces escritas en (y para) C. No importa lo que se construya además de eso, Java necesita JNI, Perl necesita llamadas en C,. NET necesita llamadas C, Python necesita llamadas C, Objective-C necesita llamadas C. Ninguno de ellos necesita llamadas de C ++, que es el punto que estaba tratando de hacer.
DevSolar
@DevSolar Muchas de las cosas de Android están escritas de forma nativa en Java y no usan JNI (puedes usarlo "al revés" para llamar a código Java desde C, pero eso solo confirma que es Java de forma nativa). No hay experiencia con iOS / OSX, pero escuché que son lo mismo con Objective-C.
user253751
3
@immibis: Pero usted lo hace saber la diferencia entre un núcleo del sistema operativo y su espacio de usuario, ¿verdad? Dudo seriamente que un espacio de usuario mayormente Java convierta a Android en un kernel de Linux con menos llamadas al sistema de estilo C que el kernel de Linux que se ejecuta en mi escritorio. También dudo que estén usando Java en el kernel o en el middleware. En realidad, que están usando C allí. Es el problema de la gallina y el huevo, al revés. Se ha hecho en C antes, y por lo tanto es mucho más fácil aún hacerlo en C
DevSolar
6

Dejando de lado los detalles que otras respuestas ya proporcionan:

La razón por la que tantos lenguajes proporcionan un enlace C es que todos los sistemas operativos * nix y Windows exponen la mayor parte de su API del sistema operativo a través de una interfaz C. Por lo tanto, la implementación del lenguaje ya necesita interactuar con C para poder ejecutarse en los Oses principales. Por lo tanto, es sencillo ofrecer también comunicarse directamente con cualquier interfaz C desde el lenguaje mismo.

Martin Ba
fuente
5

No hay razón. Si la semántica que intenta expresar es fundamentalmente compatible con C y no es algo así como plantillas, no hay ninguna razón por la que pueda vincular más fácilmente si la implementación está escrita en C. De hecho, es prácticamente por definición que una interfaz C puede ser completado por cualquier implementación que pueda cumplir con el contrato binario, incluida una implementación en otro idioma. Hay lenguajes distintos de C ++ que pueden implementar contratos binarios en C que pueden funcionar de esta manera.

Lo que realmente se reduce a personas que no quieren aprender nuevos idiomas o ideas que tengan semánticas o características realmente útiles que intentan desesperadamente elegir cualquier motivo para permanecer en la era de los dinosaurios.

DeadMG
fuente
44
Creo que está en lo correcto y los votantes negativos entendieron mal la pregunta, pero en realidad su respuesta falla para resaltar ese posible malentendido: la pregunta no se trata de "una biblioteca con una interfaz C" versus "una biblioteca con una interfaz C ++", se trata de "una biblioteca escrita completamente en C" vs. "una biblioteca con una interfaz C escrita en C ++".
Doc Brown
@Snowman: Tener enlaces de C ++ problemáticos no tiene absolutamente nada que ver con ningún problema al exponer los enlaces de C ++.
DeadMG
2
¿Entonces va a elegir un lenguaje que tenga semánticas y características útiles, y luego se obligará a traducirlas a una interfaz C? Si bien las clases y las plantillas pueden ser evitables (pero cuestione por qué está usando C ++ en primer lugar), cada función con enlace C tiene que capturar excepciones en lugar de permitir que escapen al código C. Sin mencionar que cualquiera que use su biblioteca compatible con C ahora tiene que lidiar con C ++ y sus conflictos de ABI y desajustes de la biblioteca, además de los conflictos de ABI del sustrato C y los desajustes de la biblioteca.
prosfilaes
2
@prosfilaes: es trivial convertir excepciones en códigos de retorno. No necesita evitar el uso de clases, ya que se pueden reducir fácilmente a una API C y no necesita evitar el uso de plantillas en su implementación. Y sus usuarios no necesitan preocuparse por los choques de C ++ ABI a menos que estén construyendo desde la fuente, en cuyo caso, debe lidiar con el idioma de origen sin importar lo que sea.
DeadMG
44
Voté a favor no porque no necesariamente se deba escribir una biblioteca con una API C en C ++, sino porque hay buenas razones para usar C además de ser un dinosaurio. Si está escribiendo para una API en el lenguaje X, ya sea C, FORTRAN, COBOL, BLISS o Java, siempre hay un momento en que será más simple, más fácil y más correcto escribir en ese idioma que escribir en un elegante , lenguaje más divertido e interfaz entre los dos.
prosfilaes
2

Hay dos ejes principales al interactuar con otro idioma:

  • Los conceptos que la interfaz puede trasladar: ¿solo valores? referencias? genéricos?
  • cómo se implementa la interfaz en "binarios" (llamado ABI)

C tiene una ventaja sobre C ++ en esos dos frentes:

  • C solo tiene conceptos en su mayoría simples, que aparecen en la mayoría de los demás idiomas 1
  • El sistema operativo 2 decide el ABI de los binarios C

Ahora, ¿por qué la mayoría de los lenguajes tienen un conjunto de conceptos similar al de C? Puede deberse a que sea "simple" o "preexistente"; sin embargo, no importa, lo importante es que sí.

Por el contrario, C ++ tiene conceptos complejos, y cada compilador decide el ABI (aunque muchos se adhieren al Itanimum ABI, excepto en Windows ...). En realidad, Herb Sutter propuso que los sistemas operativos solucionen un ABI de C ++ (según el sistema operativo) para abordar parcialmente este problema. Además, se debe tener en cuenta que es posible un FFI C ++, D lo está intentando 3 .

1 Excepto las variables ( ...), esas no son simples

2 ¿C tiene un ABI estándar?

3 Interfaz de D a código heredado de C ++

Matthieu M.
fuente
0

Básicamente se trata de la estandarización ABI. Si bien ni C ni C ++ tienen un ABI estandarizado que otros lenguajes pueden usar para interactuar entre los binarios escritos, C se ha convertido en un estándar de facto, todos lo saben y todos los demás pueden usar las mismas reglas simples que el lenguaje tiene con respecto a tipos y llamadas a funciones.

C ++ podría tener un ABI estándar, pero Stroustrup ha dicho que no ve la necesidad de uno. También dice que sería difícil obtener el consenso de los escritores de compiladores (aunque dudo que: el comité estándar de C ++ emitiría un ABI similar a los existentes y los escritores de compiladores simplemente alterarían la próxima versión de sus compiladores, que en ocasiones son incompatibles con los binarios construido con versiones antiguas de sus compiladores de todos modos: recuerdo haber recompilado un par de bibliotecas con un nuevo compilador de Sun y descubrí que no funcionaban con las antiguas)

Notarás que algunas compañías se han movido a usar un ABI estándar, Microsoft comenzó este proceso con COM en los años 90, y hoy lo han refinado en WinRT ABI (que no debe confundirse con el otro WinRT que se refiere a un tipo de sistema operativo de tabla) que permite que los programas escritos en C # se comuniquen con las bibliotecas escritas en C o C ++ (es decir, la capa del sistema operativo de Microsoft está escrita en C ++, expuesta usando WinRT y consumida por aplicaciones C # cuando llaman a cualquier rutina de tiempo de ejecución del sistema operativo)

No hay mucho que nadie pueda hacer a menos que un organismo de normalización intensifique y solucione esta situación. Microsoft obviamente ve el valor en él y ha tomado medidas para resolverlo para su plataforma.

Entonces la respuesta es realmente C no proporciona enlaces de lenguaje. Sucede que nadie los ha escuchado y los consume independientemente.

gbjbaanb
fuente
-2

Todas las respuestas no alcanzan el problema real: la compilación de C ++ introduce "cambio de nombre", por lo que los binarios son incompatibles con las llamadas de función "simples".

Todo lo de ABI es poco más que un intento de estandarizarlo.

En general, no está garantizado que pueda vincular funciones compiladas con diferentes compiladores, incluso si se apega a C ++ simple. Antes era seguro que eran incompatibles, pero hoy en día la estandarización se está arrastrando;)

OTOH C fue diseñado precisamente para ser un "ensamblaje de alto nivel" y permitir todo tipo de interfaz fácil. No debería sorprendernos que sea más adecuado para el gusto entre idiomas.

Nota al margen: el compilador C ++ original (cfront) en realidad produjo una fuente C que tuvo que ser compilada, exactamente como gcc que produce el ensamblaje "bajo el capó".

ZioByte
fuente