Interfaz para llamar funciones desde otros lenguajes de programación
Una interfaz de función externa ( FFI ) es un mecanismo mediante el cual un programa escrito en un lenguaje de programación puede llamar a rutinas o hacer uso de servicios escritos o compilados en otro. Una FFI se utiliza a menudo en contextos en los que se realizan llamadas a una biblioteca de vínculos dinámicos binaria .
Nombramiento
El término proviene de la especificación de Common Lisp , que se refiere explícitamente a la característica del lenguaje de programación que permite llamadas entre lenguajes como tal; [ cita requerida ] el término también se usa a menudo oficialmente en la documentación del intérprete y compilador de Haskell , [1] Rust , [2] PHP , [3] Python y LuaJIT ( Lua ) [4] [5] : 35. [ 6] Otros lenguajes usan otra terminología: Ada tiene enlaces de lenguaje , mientras que Java tiene Java Native Interface (JNI) o Java Native Access (JNA). La interfaz de función externa se ha convertido en una terminología genérica para los mecanismos que brindan tales servicios.
Operación
La función principal de una interfaz de función externa es unir la semántica y las convenciones de llamada de un lenguaje de programación (el lenguaje anfitrión o el lenguaje que define la FFI) con la semántica y las convenciones de otro (el lenguaje invitado ). Este proceso también debe tener en cuenta los entornos de ejecución y las interfaces binarias de aplicación de ambos. Esto se puede hacer de varias maneras:
- Requerir que las funciones del lenguaje invitado que se van a poder llamar desde el lenguaje anfitrión se especifiquen o implementen de una manera particular, a menudo utilizando una biblioteca de compatibilidad de algún tipo.
- Uso de una herramienta para envolver automáticamente las funciones del idioma invitado con el código de unión apropiado , que realiza cualquier traducción necesaria.
- Uso de una biblioteca contenedora
- Restringir el conjunto de capacidades del lenguaje anfitrión que se pueden utilizar en varios lenguajes. Por ejemplo, las funciones de C++ llamadas desde C no pueden (en general) incluir parámetros de referencia ni generar excepciones.
Las IFE pueden complicarse por las siguientes consideraciones:
- Si un lenguaje admite la recolección de basura (GC) y el otro no, se debe tener cuidado de que el código del lenguaje que no admite la recolección de basura no haga nada que provoque que la recolección de basura en el otro falle. En JNI, por ejemplo, el código C que "retiene" las referencias de objetos que recibe de Java debe comunicar esta información correctamente a la máquina virtual Java o Java Runtime Environment (JRE); de lo contrario, Java puede eliminar objetos antes de que C termine con ellos. (El código C también debe liberar explícitamente su vínculo a cualquier objeto de ese tipo una vez que C ya no necesite ese objeto).
- Puede resultar difícil asignar objetos o tipos de datos complicados o no triviales de un entorno a otro.
- Es posible que no sea posible que ambos idiomas mantengan referencias a la misma instancia de un objeto mutable, debido al problema de mapeo mencionado anteriormente.
- Es posible que uno o ambos idiomas se estén ejecutando en una máquina virtual (VM); además, si es así, a menudo se trata de VM diferentes.
- La herencia entre lenguajes y otras diferencias, como entre sistemas de tipos o entre modelos de composición de objetos , pueden resultar especialmente difíciles.
Algunos ejemplos de IFE incluyen:
- Enlaces del lenguaje Ada , que permiten no solo llamar a funciones externas sino también exportar sus funciones y métodos para ser llamados desde código que no sea Ada. [7]
- C++ tiene una FFI trivial con C , ya que los lenguajes comparten un subconjunto común significativo. El efecto principal de la declaración extern "C" en C++ es deshabilitar la manipulación de nombres de C++ . Con otros lenguajes, se utilizan utilidades o middleware independientes, por ejemplo:
- Clean proporciona una FFI bidireccional con todos los lenguajes siguiendo C o la convención de llamada stdcall . [8] [9]
- Ceceo común
- Interfaz nativa compilada (CNI), alternativa a JNI utilizada en el entorno del compilador GNU.
- Una de las bases del Modelo de Objetos Componentes es un formato de interfaz común, que utiliza de forma nativa los mismos tipos que Visual Basic para cadenas y matrices.
- D lo hace de la misma manera que C++ , con extern "C" hasta extern (C++)
- Dart incluye la biblioteca dart:ffi [10] para llamar a código C nativo para aplicaciones móviles, de línea de comandos y de servidor.
- Los lenguajes de programación dinámicos , como Python , Perl , Tcl y Ruby , proporcionan acceso fácil al código nativo escrito en C, C++ o cualquier otro lenguaje que obedezca las convenciones de llamada de C/C++.
- Factor tiene FFI para C, Fortran, Objective-C y Windows COM; todos estos permiten importar y llamar dinámicamente bibliotecas compartidas arbitrarias.
- Fortran 2003 tiene un módulo ISO_C_BINDING que proporciona tipos de datos interoperables (tanto tipos intrínsecos como estructuras POD), punteros interoperables, almacenes de datos globales interoperables y mecanismos para llamar a C desde Fortran y para llamar a Fortran desde C. [11] Se ha mejorado en el estándar Fortran 2018.
- Go puede llamar al código C directamente a través del
"C"
pseudopaquete. [12] - Google Web Toolkit (GWT), en el que se compila Java en JavaScript, tiene una FFI denominada JSNI que permite que el código fuente de Java llame a funciones arbitrarias de JavaScript y que JavaScript llame a Java.
- Haskell
- Interfaz nativa de Java (JNI), que proporciona una interfaz entre Java y C/C++, los lenguajes de sistemas preferidos en la mayoría de los sistemas donde se implementa Java. Java Native Access (JNA) proporciona una interfaz con bibliotecas nativas sin necesidad de escribir código de unión . Otro ejemplo es JNR
- LuaJIT, una implementación justo a tiempo de Lua , tiene una FFI que permite "llamar a funciones C externas y usar estructuras de datos C desde código Lua puro". [4] [5] : 35
- Nim tiene una interfaz FFI que le permite utilizar código fuente de C , C++ y Objective-C . También puede interactuar con JavaScript.
- JavaScript generalmente se ejecuta dentro de entornos de ejecución de navegadores web que no brindan acceso directo a bibliotecas del sistema o comandos para ejecutar, pero hay algunas excepciones:
- Node.js proporciona funciones para abrir
.node
módulos precompilados que a su vez pueden proporcionar acceso a recursos no integrados. - Deno , proporciona un tipo de interfaz FFI a través de
dlopen(...)
funciones. [13] - Bun proporciona un módulo integrado,
bun:ffi
, para llamar de manera eficiente a bibliotecas nativas directamente desde JavaScript. [14]
- Julia tiene
ccall
palabras clave para llamar a C (y otros lenguajes, por ejemplo, Fortran); [15] mientras que los paquetes, que proporcionan un soporte similar sin código repetitivo, están disponibles para algunos lenguajes, por ejemplo, para Python [16] (para proporcionar, por ejemplo, soporte OO y soporte GC), Java (y es compatible con otros lenguajes JDK, como Scala) y R. El uso interactivo con C++ también es posible con el paquete Cxx.jl. - PhoneGap (antes llamado Apache Callback, pero ahora es Apache Cordova ) es una plataforma para crear aplicaciones móviles nativas usando HTML, CSS y JavaScript. Además, tiene FFIs a través de funciones de devolución de llamada de JavaScript para acceder a métodos y propiedades de las funciones nativas del teléfono móvil, incluyendo acelerómetro, cámara (también PhotoLibrary y SavedPhotoAlbum), brújula, almacenamiento (base de datos SQL y localStorage), notificación, medios y captura (reproducción y grabación o audio y video), archivo, contactos (libreta de direcciones), eventos, dispositivo e información de conexión.[1],[2].
- PHP proporciona FFI a C. [17]
- Python proporciona los módulos ctypes y cffi. Por ejemplo, el módulo ctypes puede cargar funciones C desde una biblioteca compartida o una biblioteca de vínculos dinámicos (DLL) sobre la marcha y traducir tipos de datos simples automáticamente entre la semántica de Python y C de la siguiente manera:
import ctypes libc = ctypes . CDLL ( '/lib/libc.so.6' ) # En Linux/Unix t = libc . time ( None ) # Código C equivalente: t = time(NULL) print ( t )
- P/Invoke , que proporciona una interfaz entre Microsoft Common Language Runtime y el código nativo.
- Racket tiene una FFI nativa basada en gran medida en macros que permite importar bibliotecas compartidas arbitrarias de forma dinámica. [18] [19]
- Raku puede llamar a Ruby , Python , Perl , Brainfuck , Lua , C , C++ , Go , Scheme ( Guile , Gambit ) y Rust [20] [21]
- Ruby proporciona FFI a través de la gema ffi o a través de la biblioteca estándar fiddle.
requiere 'violín' libm = Violín . dlopen ( '/lib/libm.so.6' ) # Equivalente a: double floor(double x); floor = Fiddle :: Function.new ( libm.sym ( ' floor' ), # ptr es una función referenciada(o símbolo), de un Fiddle::Handle. [ Fiddle :: TYPE_DOUBLE ] , # args es una matriz de argumentos, que se pasa a la función ptr.Fiddle :: TYPE_DOUBLE # ret_type es el tipo de retorno de la función ) # Equivalente a: piso(3.14159); piso . call ( 3 . 14159 ) #=> 3.0
- Rust define una interfaz de función externa para funciones con varias interfaces binarias de aplicación (ABI) estándar. [22] También existe una biblioteca para interactuar con Elixir , Rustler.
- Visual Basic tiene una sintaxis declarativa que le permite llamar a funciones C no Unicode.
- Wolfram Language proporciona una tecnología llamada Wolfram Symbolic Transfer Protocol (WSTP) que permite la llamada bidireccional de código entre otros lenguajes con enlaces para C++, Java, .NET y otros lenguajes.
- Zig proporciona FFI a C utilizando la
cImport
función incorporada. [23]
Además, muchas FFI se pueden generar automáticamente: por ejemplo, SWIG . Sin embargo, en el caso de un lenguaje de extensión , puede ocurrir una inversión semántica de la relación entre el huésped y el anfitrión, cuando un cuerpo más pequeño del lenguaje de extensión es el huésped que invoca servicios en el cuerpo más grande del lenguaje anfitrión, como escribir un pequeño complemento [24] para GIMP. [25]
Algunas FFI están restringidas a funciones independientes , mientras que otras también permiten llamadas de funciones integradas en un objeto o clase (a menudo llamadas llamadas de método ); algunas incluso permiten la migración de tipos de datos u objetos complejos a través del límite del lenguaje.
En la mayoría de los casos, una FFI se define mediante un lenguaje de nivel superior , de modo que puede emplear servicios definidos e implementados en un lenguaje de nivel inferior , normalmente un lenguaje de programación de sistemas como C o C++ . Esto se hace normalmente para acceder a los servicios del sistema operativo (OS) en el lenguaje en el que se define la API del SO o por objetivos de rendimiento.
Muchas FFI también proporcionan los medios para que el idioma llamado invoque servicios también en el idioma anfitrión.
El término interfaz de función externa no se utiliza generalmente para describir entornos de ejecución multilingües como Microsoft Common Language Runtime , donde se proporciona un sustrato común que permite que cualquier lenguaje compatible con CLR utilice servicios definidos en cualquier otro. (Sin embargo, en este caso, CLR incluye una interfaz de función externa, P/Invoke , para llamar fuera del entorno de ejecución). Además, muchas arquitecturas de computación distribuida como la invocación de método remoto (RMI) de Java, RPC, CORBA , SOAP y D-Bus permiten escribir diferentes servicios en diferentes lenguajes; dichas arquitecturas generalmente no se consideran FFI.
Casos especiales
Existen algunos casos especiales en los que los lenguajes compilan en la misma máquina virtual de bytecode, como Clojure y Java , así como Elixir y Erlang . Como no hay interfaz, no es una FFI, estrictamente hablando, aunque ofrece las mismas funciones al usuario.
Véase también
Referencias
- ^ "Introducción a FFI". HaskellWiki . Consultado el 19 de junio de 2015 .
FFI de Haskell se utiliza para llamar a funciones de otros lenguajes (básicamente C en este momento) y para que C llame a funciones de Haskell.
- ^ "std::ffi". Rust-lang.org . Consultado el 1 de abril de 2021 .
Este módulo proporciona utilidades para manejar datos en interfaces que no son de Rust, como otros lenguajes de programación y el sistema operativo subyacente. Se utiliza principalmente para enlaces FFI (Foreign Function Interface) y código que necesita intercambiar cadenas similares a C con otros lenguajes.
- ^ "Manual de PHP FFI". Manual de PHP . Consultado el 31 de agosto de 2023 .
Las variables C definidas se ponen a disposición como propiedades de la instancia FFI.
- ^ de Mike Pall. "Biblioteca FFI". Luajit.org . Consultado el 29 de septiembre de 2013 .
- ^ ab Heintz, Joachim; Hofmann, Alex; McCurdy, Iain (2013). Caminos por delante: Actas de la primera conferencia internacional de Csound. Newcastle upon Tyne: Cambridge Scholars Publishing. ISBN 978-1-4438-5122-0.OCLC 855505215 .
- ^ "Documentación CFFI" . Consultado el 19 de junio de 2015 .
Interfaz de función externa C para Python. El objetivo es proporcionar una forma conveniente y confiable de llamar código C compilado desde Python utilizando declaraciones de interfaz escritas en C.
- ^ "Interfaz con otros idiomas". Adaic.org . Consultado el 29 de septiembre de 2013 .
- ^ "Exportación extranjera" . Consultado el 25 de mayo de 2020 .
- ^ "Llamando a C desde Clean" . Consultado el 25 de abril de 2018 .
- ^ "biblioteca dart:ffi" . Consultado el 1 de enero de 2020 .
- ^ "wiki de etiqueta 'fortran-iso-c-binding'". Desbordamiento de pila .
- ^ "cgo". Lenguaje de programación Go . Consultado el 23 de agosto de 2015 .
- ^ "Interfaz de función externa | Manual". Deno . Consultado el 8 de febrero de 2023 .
- ^ "API de FFI". Documentos de Bun .
- ^ "Llamando a C y al código Fortran". JuliaLang.org . Consultado el 11 de febrero de 2018 .
- ^ PyCall.jl: Paquete para llamar a funciones Python desde el lenguaje Julia, JuliaPy, 2018-02-08 , consultado el 2018-02-11
- ^ "PHP: FFI - Manual". The PHP Group . Consultado el 13 de junio de 2019 .
- ^ Eli Barzilay. "La interfaz extranjera de la raqueta". Docs.racket-lang.org . Consultado el 29 de septiembre de 2013 .
- ^ "TR600.pdf" (PDF) . Archivado desde el original (PDF) el 2009-09-02 . Consultado el 2013-09-29 .
- ^ "Implementaciones en línea" . Consultado el 15 de agosto de 2017 .
- ^ "Llamada nativa" . Consultado el 15 de agosto de 2017 .
- ^ "Uso de funciones externas para llamar a código externo" . Consultado el 1 de junio de 2019 .
- ^ "Importar desde archivo de encabezado C". Zig Software Foundation . Consultado el 11 de marzo de 2021 .
- ^ "4. Un script de muestra". Gimp.org. 2001-02-04 . Consultado el 2013-09-29 .
- ^ "Script-Fu y complementos para The GIMP". Gimp.org . Consultado el 29 de septiembre de 2013 .
Enlaces externos
- c2.com: Interfaz de función externa
- Interfaz de función externa de Haskell 98
- Allegro Common Lisp FFI
- Un generador de interfaz de función externa para occam-pi
- UFFI: Interfaz universal de funciones externas de Lisp
- CFFI: Interfaz de función externa común, para Common Lisp
- Interfaz nativa de Java: guía y especificaciones del programador
- La especificación JNI
- JSNI (Interfaz nativa de JavaScript)
- Biblioteca dyncall que utiliza núcleos de llamadas de ensamblaje para una variedad de procesadores, sistemas operativos y convenciones de llamadas
- Llamada de emergencia
- C/Invocar
- libffi