Интерфейс для вызова функций из других языков программирования
Интерфейс внешних функций ( FFI ) — это механизм, с помощью которого программа, написанная на одном языке программирования, может вызывать процедуры или использовать службы, написанные или скомпилированные на другом языке. FFI часто используется в контекстах, где вызовы производятся в бинарную динамическую библиотеку .
Нейминг
Термин происходит из спецификации Common Lisp , которая явно ссылается на функцию языка программирования, позволяющую осуществлять межъязыковые вызовы как таковые; [ требуется ссылка ] термин также часто официально используется в документации интерпретатора и компилятора для Haskell , [1] Rust , [2] PHP , [3] Python и LuaJIT ( Lua ) [4] [5] : 35. [ 6] Другие языки используют другую терминологию: Ada имеет языковые привязки , в то время как Java имеет Java Native Interface (JNI) или Java Native Access (JNA). Интерфейс внешних функций стал общим термином для механизмов, которые предоставляют такие услуги.
Операция
Основная функция интерфейса внешней функции — сопоставить семантику и соглашения о вызовах одного языка программирования ( языка- хозяина или языка, определяющего FFI) с семантикой и соглашениями другого ( гостевого языка). Этот процесс также должен учитывать среды выполнения и двоичные интерфейсы приложений обоих. Это можно сделать несколькими способами:
- Требование, чтобы функции гостевого языка, которые должны вызываться на языке хоста, были указаны или реализованы определенным образом, часто с использованием какой-либо библиотеки совместимости.
- Использование инструмента для автоматического оборачивания функций гостевого языка соответствующим связующим кодом , который выполняет любой необходимый перевод.
- Использование библиотеки-обертки
- Ограничение набора возможностей языка хоста, которые могут использоваться на разных языках. Например, функции C++, вызываемые из C, не могут (в общем случае) включать ссылочные параметры или выдавать исключения.
FFI могут быть осложнены следующими факторами:
- Если один язык поддерживает сборку мусора (GC), а другой нет; необходимо позаботиться о том, чтобы код языка, не поддерживающего GC, не делал ничего, что могло бы привести к сбою GC в другом языке. Например, в JNI код C, который «удерживает» ссылки на объекты, которые он получает от Java, должен успешно передавать эту информацию виртуальной машине Java или среде выполнения Java (JRE), в противном случае Java может удалить объекты до того, как C закончит с ними. (Код C также должен явно освободить свою ссылку на любой такой объект, как только C больше не будет нуждаться в этом объекте.)
- Сложные или нетривиальные объекты или типы данных может быть трудно преобразовать из одной среды в другую.
- Из-за описанной выше проблемы отображения может оказаться невозможным для обоих языков поддерживать ссылки на один и тот же экземпляр изменяемого объекта.
- Один или оба языка могут работать на виртуальной машине (ВМ); более того, если работают оба языка, то это часто разные ВМ.
- Особую сложность может представлять межъязыковое наследование и другие различия, например, между системами типов или моделями композиции объектов .
Примеры FFI включают в себя:
- Связывания языка Ada , позволяющие не только вызывать сторонние функции, но и экспортировать свои функции и методы для вызова из кода, отличного от Ada. [7]
- C++ имеет тривиальный FFI с C , поскольку эти языки разделяют значительное общее подмножество. Основной эффект объявления extern "C" в C++ заключается в отключении искажения имен C++ . С другими языками используются отдельные утилиты или промежуточное ПО, примеры включают:
- Clean обеспечивает двунаправленный FFI со всеми языками, следующими за C или соглашением о вызовах stdcall . [8] [9]
- Общий Лисп
- Скомпилированный собственный интерфейс (CNI), альтернатива JNI, используемая в среде компилятора GNU.
- Одной из основ компонентной объектной модели является общий формат интерфейса, который изначально использует те же типы, что и Visual Basic для строк и массивов.
- D делает это так же, как и C++ , с extern "C" через extern (C++)
- Dart включает библиотеку dart:ffi [10] для вызова собственного кода C для мобильных, командных и серверных приложений.
- Динамические языки программирования , такие как Python , Perl , Tcl и Ruby , обеспечивают простой доступ к машинному коду, написанному на C, C++ или любом другом языке, подчиняющемся соглашениям о вызовах C/C++.
- Factor имеет FFI для C, Fortran, Objective-C и Windows COM; все они позволяют импортировать и вызывать произвольные общие библиотеки динамически.
- В Fortran 2003 есть модуль ISO_C_BINDING, который предоставляет совместимые типы данных (как внутренние типы, так и структуры POD), совместимые указатели, совместимые глобальные хранилища данных и механизмы для вызова C из Fortran и для вызова Fortran из C. [11] Он был улучшен в стандарте Fortran 2018.
- Go может вызывать код C напрямую через
"C"
псевдопакет. [12] - Google Web Toolkit (GWT), в котором Java компилируется в JavaScript, имеет FFI под названием JSNI, который позволяет исходному коду Java вызывать произвольные функции JavaScript, а JavaScript — выполнять обратный вызов Java.
- Хаскелл
- Java Native Interface (JNI), который обеспечивает интерфейс между Java и C/C++, предпочтительными системными языками в большинстве систем, где развернута Java. Java Native Access (JNA) обеспечивает интерфейс с собственными библиотеками без необходимости писать связующий код . Другим примером является JNR
- LuaJIT, реализация Lua «точно в срок» , имеет FFI, который позволяет «вызывать внешние функции C и использовать структуры данных C из чистого кода Lua». [4] [5] : 35
- Nim имеет FFI, который позволяет использовать исходный код из C , C++ и Objective-C . Он также может взаимодействовать с JavaScript.
- JavaScript обычно выполняется внутри среды выполнения веб-браузера , которая не предоставляет прямого доступа к системным библиотекам или командам для запуска, но есть несколько исключений:
- Node.js предоставляет функции для открытия предварительно скомпилированных
.node
модулей, которые, в свою очередь, могут предоставлять доступ к не встроенным ресурсам. - Deno , обеспечивает своего рода интерфейс FFI через
dlopen(...)
функции. [13] - Bun предоставляет встроенный модуль
bun:ffi
, для эффективного вызова собственных библиотек непосредственно из JavaScript. [14]
- Julia имеет
ccall
ключевое слово для вызова C (и других языков, например, Fortran); [15] в то время как пакеты, обеспечивающие аналогичную поддержку без шаблонов, доступны для некоторых языков, например, для Python [16] (например, для обеспечения поддержки OO и поддержки GC), Java (и поддерживает другие языки JDK, такие как Scala) и R. Интерактивное использование с C++ также возможно с пакетом Cxx.jl. - PhoneGap (раньше назывался Apache Callback, но теперь Apache Cordova ) — это платформа для создания собственных мобильных приложений с использованием HTML, CSS и JavaScript. Кроме того, он имеет FFI через функции обратного вызова JavaScript для доступа к методам и свойствам собственных функций мобильного телефона, включая акселерометр, камеру (также PhotoLibrary и SavedPhotoAlbum), компас, хранилище (база данных SQL и localStorage), уведомления, медиа и захват (воспроизведение и запись или аудио и видео), файлы, контакты (адресная книга), события, устройства и информацию о подключении.[1],[2].
- PHP предоставляет FFI для C. [17]
- Python предоставляет модули ctypes и cffi. Например, модуль ctypes может загружать функции C из общей библиотеки или библиотеки динамической компоновки (DLL) на лету и автоматически транслировать простые типы данных между семантикой Python и C следующим образом:
import ctypes libc = ctypes . CDLL ( '/lib/libc.so.6' ) # Под Linux/Unix t = libc . time ( None ) # Эквивалентный код на языке C: t = time(NULL) print ( t )
- P/Invoke , который обеспечивает интерфейс между средой выполнения Microsoft Common Language Runtime и собственным кодом.
- Racket имеет собственный FFI, основанный в значительной степени на макросах, который позволяет динамически импортировать произвольные общие библиотеки. [18] [19]
- Raku может вызывать Ruby , Python , Perl , Brainfuck , Lua , C , C++ , Go , Scheme ( Guile , Gambit ) и Rust [20] [21]
- Ruby предоставляет FFI либо через ffi gem, либо через стандартную библиотеку fiddle.
требуется «скрипка» libm = Скрипка . dlopen ( '/lib/libm.so.6' ) # Эквивалентно: double floor(double x); floor = Fiddle :: Function . new ( libm . sym ( 'floor' ), # ptr — это ссылочная функция (или символ) Fiddle::Handle. [ Fiddle :: TYPE_DOUBLE ] , # args — это массив аргументов, передаваемых функции ptr. Fiddle :: TYPE_DOUBLE # ret_type — это возвращаемый тип функции ) # Эквивалентно: floor(3.14159); floor.call ( 3.14159 ) # = > 3.0
- Rust определяет внешний функциональный интерфейс для функций с различными стандартными двоичными интерфейсами приложений (ABI). [22] Также существует библиотека для взаимодействия с Elixir , Rustler.
- Visual Basic имеет декларативный синтаксис, который позволяет вызывать функции C, не поддерживающие Unicode.
- Wolfram Language предоставляет технологию под названием Wolfram Symbolic Transfer Protocol (WSTP), которая обеспечивает двунаправленный вызов кода между другими языками с привязками к C++, Java, .NET и другим языкам.
- Zig предоставляет FFI для C с помощью встроенной
cImport
функции. [23]
Кроме того, многие FFI могут быть сгенерированы автоматически: например, SWIG . Однако в случае языка расширения может возникнуть семантическая инверсия отношений гостя и хоста, когда меньший корпус языка расширения является гостем, вызывающим службы в большем корпусе языка хоста, например, написание небольшого плагина [24] для GIMP. [25]
Некоторые FFI ограничены отдельными функциями , в то время как другие также допускают вызовы функций, встроенных в объект или класс (часто называемые вызовами методов ); некоторые даже допускают миграцию сложных типов данных или объектов через границу языка.
В большинстве случаев FFI определяется языком более высокого уровня , так что он может использовать службы, определенные и реализованные на языке более низкого уровня , как правило, на языке системного программирования , таком как C или C++ . Обычно это делается либо для доступа к службам операционной системы (ОС) на языке, на котором определен API ОС, либо для целей производительности.
Многие FFI также предоставляют средства для вызываемого языка, чтобы вызывать сервисы на принимающем языке.
Термин «интерфейс внешних функций» обычно не используется для описания многоязычных сред выполнения, таких как Microsoft Common Language Runtime , где предоставляется общий субстрат , который позволяет любому языку, совместимому с CLR, использовать службы, определенные в любом другом. (Однако в этом случае CLR включает FFI, P/Invoke , для вызова за пределами среды выполнения.) Кроме того, многие распределенные вычислительные архитектуры, такие как удаленный вызов методов Java (RMI), RPC, CORBA , SOAP и D-Bus, позволяют писать различные службы на разных языках; такие архитектуры, как правило, не считаются FFI.
Особые случаи
Есть некоторые особые случаи, в которых языки компилируются в одну и ту же виртуальную машину байт-кода, как Clojure и Java , а также Elixir и Erlang . Поскольку нет интерфейса, это не FFI, строго говоря, хотя он предлагает те же функции пользователю.
Смотрите также
Ссылки
- ^ "Введение в FFI". HaskellWiki . Получено 19 июня 2015 г.
FFI языка Haskell используется для вызова функций из других языков (в основном C на данный момент), а также для вызова функций языка Haskell из языка C.
- ^ "std::ffi". Rust-lang.org . Получено 1 апреля 2021 г.
Этот модуль предоставляет утилиты для обработки данных через интерфейсы, отличные от Rust, такие как другие языки программирования и базовая операционная система. Он в основном используется для привязок FFI (Foreign Function Interface) и кода, которому необходимо обмениваться строками в стиле C с другими языками.
- ^ "PHP FFI Manual". PHP Manual . Получено 31 августа 2023 г. Определенные
переменные C доступны как свойства экземпляра FFI.
- ^ ab Майк Полл. "Библиотека FFI". Luajit.org . Получено 29.09.2013 .
- ^ ab Heintz, Joachim; Hofmann, Alex; McCurdy, Iain (2013). Пути вперед: Труды первой международной конференции Csound. Ньюкасл-апон-Тайн: Cambridge Scholars Publishing. ISBN 978-1-4438-5122-0. OCLC 855505215.
- ^ "CFFI documentation" . Получено 19 июня 2015 г.
Интерфейс внешних функций C для Python. Цель состоит в том, чтобы предоставить удобный и надежный способ вызова скомпилированного кода C из Python с использованием деклараций интерфейсов, написанных на C.
- ^ "Интерфейс к другим языкам". Adaic.org . Получено 29.09.2013 .
- ^ "Иностранный экспорт" . Получено 2020-05-25 .
- ^ "Вызов C из Clean" . Получено 2018-04-25 .
- ^ "библиотека dart:ffi" . Получено 2020-01-01 .
- ^ "wiki тег 'fortran-iso-c-binding'". Переполнение стека .
- ^ "cgo". Язык программирования Go . Получено 2015-08-23 .
- ^ "Foreign Function Interface | Руководство". Deno . Получено 2023-02-08 .
- ^ "FFI API". Bun Docs .
- ^ "Вызов кода C и Fortran". JuliaLang.org . Получено 2018-02-11 .
- ^ PyCall.jl: Пакет для вызова функций Python из языка Julia, JuliaPy, 2018-02-08 , получено 2018-02-11
- ^ "PHP: FFI - Руководство". PHP Group . Получено 13 июня 2019 г.
- ^ Эли Барзилай. "The Racket Foreign Interface". Docs.racket-lang.org . Получено 29.09.2013 .
- ^ "TR600.pdf" (PDF) . Архивировано из оригинала (PDF) 2009-09-02 . Получено 2013-09-29 .
- ^ "Встроенные реализации" . Получено 2017-08-15 .
- ^ "Native Call" . Получено 2017-08-15 .
- ^ "Использование внешних функций для вызова внешнего кода" . Получено 01.06.2019 .
- ^ "Импорт из файла заголовка C". Zig Software Foundation . Получено 2021-03-11 .
- ^ "4. Пример сценария". Gimp.org. 2001-02-04 . Получено 2013-09-29 .
- ^ "Script-Fu и плагины для GIMP". Gimp.org . Получено 29.09.2013 .
Внешние ссылки
- c2.com: Интерфейс внешней функции
- Интерфейс внешних функций Haskell 98
- Allegro Common Lisp FFI
- Генератор интерфейса внешних функций для occam-pi
- UFFI: Универсальный интерфейс внешних функций Lisp
- CFFI: Общий интерфейс внешних функций для Common Lisp
- Java Native Interface: Руководство программиста и спецификация
- Спецификация JNI
- JSNI (собственный интерфейс JavaScript)
- Библиотека dyncall, использующая ядра вызовов ассемблера для различных процессоров, ОС и соглашений о вызовах
- FFCALL
- C/Вызов
- libffi