Взаимодействие с Си¶
В Ü имеется ограниченная возможность взаимодействовать с кодом на Си. Можно вызывать функции, написанные на Си из Ü. Также можно вызывать функции, написанные на Ü из Си.
Для взаимодействия с Си в Ü есть ряд механизмов.
Функции без кодирования имён¶
В отличие от Си, в Ü имена обычных функции специальным образом кодируются, чтобы итоговые полные имена перегруженных функций или функций в различных пространствах имён различались.
Чтобы отменить такое кодирование, надо пометить функцию модификатором nomangle
, указанным после fn
и опционального модификатора constexpr
.
Имя функции, помещенное таким образом, будет тем же, каким бы оно было в Си. Соответственно, такую функцию можно будет вызвать из Си.
Также можно будет пометить таким образом прототип функции, реализованной на Си, для возможности вызова её из Ü.
fn nomangle SDL_Quit() : void;
fn MyQuit()
{
SDL_Quit(); // Вызываем внешнюю функцию
}
// Функция, доступная в Си коде
fn nomangle SomeFoo() : i32
{
return 0;
}
nomangle
функции имеют ряд ограничений:
Операторы не могут быть
nomangle
Функции-члены структур или классов не могут быть
nomangle
nomangle
функции могут быть расположены только в глобальном пространстве имёнНельзя перегружать
nomangle
функции
Cоглашения о вызове¶
Функции и указатели на функию могут иметь указание соглашения о вызове.
Оно указывается после перечисления списка параметров функции и после unsafe
(если таковой есть).
fn Foo() call_conv("C");
fn Bar(i32 x) unsafe call_conv("fast") {}
fn nomangle Baz(f32 x, f32 y) call_conv("system"){}
var (fn() call_conv("C")) ptr = Foo;
Существуют следующие имена соглашений о вызовах: C
, default
, Ü
, fast
, cold
, system
.
C
- стандартное соглашение о вызове, использующееся в языках Си и C++.
default
и Ü
это псевдонимы для C
.
Также, если не указано соглашение о вызове, оно будет равно C
.
Для взаимодействия с Си и C++ кодом следует использовать именно это соглашение о вызове.
fast
и cold
- соглашения для соответственно быстрого или компактного вызова.
Они не совместимы со схожими соглашениями в других языках, так что использовать их можно только в Ü коде, и то, если это даёт ощутимый эффект.
system
- это платформозависимый псевдоним для системных функций.
На большинстве платформ это псевдоним для соглашения C
.
На 32-битной Windows это псевдоним для stdcall
соглашения, которое используется в WinAPI.
Структуры с упорядоченными полями¶
Обычные структуры и классы в Ü имеют неопределённый порядок полей. Компилятор имеет право упорядочить их, как ему удобно.
В Си же поведение иное - поля структур там упорядочены в порядке объявления.
Для того, чтобы получить порядок полей структур, как в Си, существует специальный модификатор - ordered
.
Данный модификатор указывается при объявлении структуры или класса, после опциональных вида класса, родительских классов, модификатора non_sync
.
Структура объявленная с таким модификатором будет иметь тот же порядок полей, что аналогичная структура в Си.
В примере ниже две данные структуры будут аналогичны.
struct A ordered
{
bool x;
i32 y;
bool z;
}
struct A
{
bool x;
int32_t y;
bool z;
};
Внешние функции¶
В Ü существует специальный оператор, который предназначен для доступа к внешних функциям (объявленным вне Ü кода).
Он состоит из ключевых слов import
и fn
, после которых следует имя типа функции в <//>
и имя функции в виде строки внутри ()
.
Этот оператор возвращает указатель на запрашиваемую функцию.
fn Foo()
{
unsafe
{
auto f= import fn</ fn() : i32 />( "_some_external_function" ); // Получаем указатель на требуемую функцию
var i32 x= f(); // Производим вызов
}
}
Использовать этот оператор разрешено только в unsafe
блоках и выражениях.
Это необходимо, т. к. программист должен гарантировать, что указанный тип совместим с таковым во внешнем коде и что нету конфликта имён с функциями, написанными на Ü.
Этот оператор предназначен для тех случаев, когда нельзя просто написать прототип для некой внешней функции.
Такое может быть, если имя функции не является корректным именем в Ü, например начинается с _
, содержит запрещённые символы или является ключевым словом.
Внешние глобальные переменные¶
В Ü существует специальный оператор, который предназначен для доступа к внешним глобальным переменным (объявленным вне Ü кода).
Он состоит из ключевых слов import
и var
, после которых следует имя типа переменной в <//>
и имя переменной в виде строки внутри ()
.
Этот оператор возвращает изменяемую ссылку на запрашиваемую переменную.
fn Foo()
{
unsafe
{
var i32 &mut x= import var</ i32 />( "__some_var" ); // Получаем ссылку на требуемую переменную
++x; // Можем изменять переменную
}
}
Данный оператор необходим для доступа ко внешним переменным (объявленным вне Ü кода), т. к. других средств в языке для этого нету.
Возвращаемая ссылка всегда изменяемая, если внешняя переменная объявлена как неизменяемая, изменять её через эту изменяемую ссылку не разрешается.
Thread-local переменные не поддерживаются.
Использовать этот оператор нужно с осторожностью.
Его использование разрешено только в unsafe
блоках и выражениях.
Ограничения и предостережения взаимодействия с Си¶
Не каждую функцию на Ü можно вызвать из Си и наоборот. Существует ряд ограничений, накладываемых на вызов. Компилятор Ü не знает, что какая-та функция реализована на Си, и поэтому не может проверить правильность вызова. Поэтому соблюдение правил возложено на программиста.
Аргументы-значения и возвращаемые значения функций должны быть фундаментального типа, типа перечисления, типа указателя на функцию или сырого указателя. Составные типы (структуры/классы, массивы, кортежи) для значений не поддерживаются. При этом можно передавать в функцию и возвращать из функции ссылки, они аналогичны указателям в Си.
Структуры, передаваемые в Си код, или получаемые из него, должны иметь одинаковый состав и порядок полей в Ü и в Си. Исключения составляют структуры, к полям которых нету обращений из Ü и которые передаются в Си по одиночке (не массивом). Такие структуры могут не иметь аналогичного состава полей, главное, чтобы их размер и выравнивание были не меньше, чем в Си.
Кортежи в Ü по размещению в памяти аналогичны структурам в Си, с тем же набором и порядком типов элементов, что в кортеже. Поэтому в Си коде следует использовать структуры-аналоги для кортежей Ü.
В Си коде не действует контроль ссылок. Поэтому надо быть особо внимательным при взаимодействии с Си кодом.
Для большей безопасности рекомендуется помечать как unsafe
функции, реализованные на Си.
Это заставит пользователя таких функций оборачивать вызов этих функций в unsafe
блок и придаст больше внимательности.