van rossum programacion payton lenguaje guido español descargar curso caracteristicas java python binding

java - rossum - ¿Cómo se vincula un lenguaje(python, por ejemplo) a otro(por ejemplo, C++)?



python lenguaje (6)

Estoy lejos de ser un experto en python, pero escucho esto todo el tiempo, sobre sus enlaces C / C ++. ¿Cómo funciona este concepto, y cómo se une Python (y Java) a las API basadas en C como OpenGL? Esto siempre ha sido un misterio para mí.


Básicamente, hay dos formas de integrar c / c ++ con python:

  • extendiendo: accediendo a c / c ++ desde python
  • incrustación: acceder al intérprete de Python desde c / c ++

Lo que mencionas es el primer caso. Normalmente se logra escribiendo funciones de contenedor que sirve como código de pegamento entre los diferentes idiomas que convierte los argumentos de la función y los tipos de datos para que coincidan con el idioma necesario. Usualmente se usa una herramienta llamada SWIG para generar este código de pegamento.

Para una explicación extensa, mira este tutorial .


El concepto general principal se conoce como FFI , "Foreign Function Interface" - para Java es JNI, para Python es la "Python C API", para Perl es XS, etc., pero creo que es importante darle la información general. término de arte para ayudarlo a investigarlo más a fondo.

Dado un FFI, puede escribir (p. Ej.) Programas C que lo respetan directamente, y / o puede tener generadores de código que produzcan dicho código C a partir de metainformación que reciben y / o introspección de código escrito en otros idiomas (a menudo con alguna ayuda, por ejemplo, para manejar el generador de código SWIG, normalmente decora la información que está en un archivo de cabecera .h C con información adicional que es específica de SWIG para obtener un mejor contenedor).

También hay lenguajes especiales como Cython , un "subconjunto extendido" de Python que está orientado a la generación fácil de código FFI, mientras que la mayoría de la sintaxis y semántica de Python - a menudo puede ser la forma más fácil para los programadores de Python escribir una extensión de Python módulo que se compila para acelerar el código de la máquina y tal vez utiliza algunas bibliotecas C-invocables existentes.

El enfoque ctypes es diferente de los enfoques FFI tradicionales, aunque se autodescribe como una "biblioteca de funciones extranjeras para Python": se basa en que el código externo está disponible en una DLL (o equivalente, como una biblioteca dinámica .so en Linux), y genera y ejecuta código en tiempo de ejecución para alcanzar ese código C cargado dinámicamente (normalmente todo se hace a través de programación explícita en Python; aún no sé de los envoltorios de ctypes basados ​​en introspección y generación de código ctypes) . Práctico para evitar tener que instalar algo especial para tareas simples de acceso a archivos DLL existentes con Python, pero creo que no escala tan bien como los enfoques FFI "basados ​​en enlazadores" (ya que requiere más esfuerzo de tiempo de ejecución, etc., etc.) . No conozco ninguna otra implementación de dicho enfoque, dirigida a otros idiomas, más allá de los tipos de Python (imagino que algunos sí existen, dada la prevalencia actual de DLL y .so packaging, y sería curioso conocerlos).


En general, estos lenguajes tienen una forma de cargar extensiones escritas en C. La interfaz de Java se llama JNI (Java Native Interface). Python tiene documentación completa sobre su interfaz de extensión.

Otra opción para Python es el módulo ctypes que le permite trabajar con librer C cargables dinámicamente sin tener que escribir código de extensión personalizado.


Los conceptos a continuación se pueden generalizar con relativa facilidad, sin embargo, me voy a referir mucho a C y Python para mayor claridad.

Llamando a C desde Python

Esto puede funcionar porque la mayoría de los lenguajes / arquitecturas / sistemas operativos de nivel inferior tienen interfaces binarias de aplicaciones bien definidas que especifican todos los detalles de bajo nivel de cómo las aplicaciones interactúan entre sí y con el sistema operativo. Como ejemplo, aquí está el ABI para x86-64 (AMD64): AMD64 System V Application Binary Interface . Especifica todos los detalles de cosas como convenciones de llamada para funciones y vinculación con archivos de objeto C.

Con esta información, depende de los implementadores del lenguaje

  1. Implemente el ABI del idioma al que desea llamar

  2. Proporcionar una interfaz a través del idioma / biblioteca para acceder a la implementación

(1) casi se obtiene gratis en la mayoría de los idiomas debido al hecho de que sus intérpretes / compiladores están codificados en C, lo que obviamente respalda el C ABI :). Esta es también la razón por la cual es difícil llamar al código C desde implementaciones de lenguajes no codificados en C, por ejemplo, IronPython (implementación de Python en C #) y PyPy (implementación de Python en Python) no tienen un soporte particularmente bueno para llamar al código C, aunque Creo que ha habido algo de trabajo con respecto a esto en IronPython.

Entonces, para hacer esto concreto, supongamos que tenemos CPython (La implementación estándar de Python, hecha en C). Obtenemos (1) de forma gratuita ya que nuestro intérprete está escrito en C y podemos acceder a las bibliotecas C de nuestro intérprete de la misma manera que lo haríamos con cualquier otro programa de C (dlopen, LoadLibrary, lo que sea). Ahora tenemos que ofrecer una forma para que las personas que escriben en nuestro idioma accedan a estas instalaciones. Python hace esto a través de The Python C / C ++ API o ctypes . Cada vez que un programador escribe código usando estas API, podemos ejecutar el código de carga / llamada de biblioteca apropiado para llamar a las bibliotecas.

Llamar a Python desde C

Esta dirección es en realidad un poco más simple de explicar. Continuando con el ejemplo anterior, nuestro intérprete, CPython no es más que un programa escrito en C, por lo que puede exportar funciones y ser compilado como una biblioteca / vinculado por cualquier programa que deseemos escribir en C. CPython exporta un conjunto de C funciones para acceder / ejecutar el programa Python y podemos llamar a estas funciones para ejecutar el código de Python desde nuestra aplicación. Por ejemplo, una de las funciones exportadas por la biblioteca CPython es:

PyObject* PyRun_StringFlags(const char *str, int start, PyObject *globals, PyObject *locals, PyCompilerFlags *flags)¶

Valor de retorno: Nueva referencia.

Ejecute el código fuente de Python desde str en el contexto especificado por los diccionarios globales y locales con los indicadores del compilador especificados por flags. El parámetro start especifica el token de inicio que se debe usar para analizar el código fuente.

Podemos, literalmente, ejecutar código Python pasando esta función una cadena que contiene código Python válido (y algunos otros detalles necesarios para la ejecución). Consulte Incrustar Python en otra aplicación para más detalles.


Para Perl, hay dos formas de llamar a las subrutinas de C ++:


Intérpretes escritos en C89 con Reflection, Who Knew?

Tengo la sensación de que está buscando una explicación del mecanismo y no un enlace a la API o instrucciones sobre cómo codificarlo. Entonces, como yo lo entiendo . .

El intérprete principal generalmente se escribe en C y está vinculado dinámicamente. En un entorno dinámicamente vinculado, incluso C89 tiene una cierta cantidad de comportamiento reflexivo. En particular, las dlopen(3) y dlsym(3) cargarán una biblioteca dinámica (típicamente ELF) y buscarán la dirección de un símbolo nombrado por una cadena. Dar esa dirección, el intérprete puede llamar a una función. Incluso si está vinculado estáticamente, el intérprete puede conocer la dirección de las funciones C cuyos nombres están compilados en ella.

Entonces, simplemente es cuestión de hacer que el código interpretado le diga al intérprete que llame a una función nativa particular en una biblioteca nativa particular.

El mecanismo puede ser modular. Una biblioteca de extensión para el intérprete, escrita en el guión, puede invocar los ganchos desnudos para dlopen(3) y dlsym(3) y conectarse a una nueva biblioteca que el intérprete nunca conoció.

Para pasar objetos simples por valor, unas pocas funciones de prototipo típicamente permitirán varias llamadas. Pero para los objetos de datos estructurados (imagine stat (2)), el módulo envoltorio necesita conocer el diseño de los datos. En algún momento, ya sea al empaquetar el módulo de extensión o al instalarlo, un módulo de interfaz C incluye los archivos de encabezado apropiados y, junto con el código manuscrito, construye un objeto de interfaz. Es por esto que puede necesitar instalar algo como libsqlite3-dev incluso si ya tiene sqlite3 en su sistema; solo el paquete -dev tiene los archivos .h necesarios para recompilar el código de vinculación.

Supongo que podríamos resumirlo diciendo: "se hace con la fuerza bruta y la ignorancia" . :-)