Перейти к основному содержимому

FFI

Этот документ описывает спецификацию FFI (интерфейс внешней функции) для вызова функций, реализованных извне, в языке Wave. С помощью FFI программы на Wave могут напрямую взаимодействовать с нативными библиотеками, написанными на других языках.


Обзор

FFI языка Wave работает на основе деклараций. Внешние функции не реализуются в коде Wave, а просто указывают на поддержку определенного ABI (Application Binary Interface). Фактическая реализация разрешается на этапе связывания из внешней библиотеки.

FFI функционирует за счёт декларации существования функции во время компиляции, а линковщик связывает фактический символ при создании исполняемого файла.


Объявление extern

Внешние функции объявляются с использованием ключевого слова extern. В настоящее время в Wave требование указания ABI является обязательным, и поддерживается только extern(c).

extern(c) fun function_name(args...) -> return_type;

Указание ABI

В объявлениях extern необходимо указывать ABI. В настоящее время поддерживается только ABI c.

extern(c) fun printf(fmt: ptr<u8>);

Объявление наподобие extern(rust) будет проанализировано, но вызовет ошибку на этапе семантического анализа.


Объявление extern на уровне функции

Для объявления одной внешней функции используйте следующий синтаксис.

extern(c) fun InitWindow(width: i32, height: i32, title: ptr<u8>);

Это объявление показывает, что символ InitWindow, следующий C ABI, присутствует во внешней библиотеке.


Объявление extern на уровне блока

Если несколько внешних функций используют одинаковый ABI, их можно объявить в виде блока.

extern(c) {
fun InitWindow(width: i32, height: i32, title: ptr<u8>);
fun CloseWindow();
fun BeginDrawing();
fun EndDrawing();
}

Объявления на уровне блока семантически полностью совпадают с объявлениями на уровне функции и являются синтаксисом, улучшающим читаемость и структуризацию.


Указание имени символа

В некоторых ABI имя функции Wave и фактическое имя символа линковки может не совпадать. В этом случае действительное имя символа, с которым будет связано внешняя функция, можно указать в виде строки.

Указание символа на уровне функции

extern(c, "puts")
fun rust_func(i32);

Это объявление определяет, что при вызове rust_func фактически используется символ puts в линковке.


Указание символа на уровне блока

В объявлениях на уровне блока имя символа можно указать отдельно после каждой функции.

extern(c) {
fun my_puts(ptr<i8>) "puts";
fun my_strlen(ptr<i8>) "strlen";
}

Тип указателя

Указатели представлены как ptr<T>.

ptr<u8>
ptr<MyStruct>

ptr<T> напрямую соответствует указателю внешнего языка, и управление правами владения памятью или жизненным циклом не осуществляется Wave.


Использование структуры

Структуры могут быть использованы в качестве аргументов или возвращаемых значений внешней функции.

struct Color {
r: u8,
g: u8,
b: u8,
a: u8,
}

При использовании структуры в FFI порядок полей сохраняется в соответствии с декларированным, и соблюдается необходимое ABI расположение памяти.


Вызов внешней функции

Функции, объявленные с помощью extern, вызываются тем же способом, что и обычные функции.

fun main() -> i32 {
InitWindow(800, 600, "Wave");
BeginDrawing();
EndDrawing();
CloseWindow();
return 0;
}

При вызове нет синтаксических различий, соглашение о вызове и связывание символов осуществляется полностью через ABI и линковку.


Линковка

Реальная реализация внешней функции предоставляется из внешней библиотеки на этапе линковки. Компилятор Wave создает объектные файлы с включением вызовов внешних функций и разрешает символы с помощью указанной библиотекой линкера.

Метод указания библиотеки осуществляется со помощью инструментов сборки и опций CLI.


Ограничения

Wave не предоставляет следующие функции.

  • Указатель на функцию
  • Функция обратного вызова
  • Автоматическое управление памятью
  • Интеграция обработки исключений между языками

Эти функции могут быть рассмотрены в последующих версиях.