Указатели на функцию¶
В Ü есть возможность сохранить адрес функции в указателе, после чего вызвать её с его использованием.
Указатель на функцию есть отдельный вид типа. Имя типа указателя на функцию начинается с ключевого слова fn
, далее идёт описание аргументов, (опционально) связывания ссылок, (опционально) модификатор unsafe
, описание возвращаемого значения.
type simple_fn = fn();
type int_ret_fn = fn() : i32;
type f_arg_fn = fn(f32 x) : f32;
type ref_ret_fn = fn(i32 &x) : i32&;
type binary_fn = fn(i64 x, i64 y) : i64;
type unsafe_fn = fn(u32 x) unsafe : bool;
Указатель на функцию надо инициализировать. Его можно инициализировать при помощи zero_init
, но при вызове такого указателя произойдёт аварийное завершение программы.
Чтобы вызывать указатель без аварийного завершения, его нужно инициализировать функцией.
fn Bar();
fn Foo()
{
var (fn()) fn_ptr = Bar;
fn_ptr(); // Будет вызвана функция "Bar"
}
При наличии нескольких функций с одним и тем же именем, использованным для инициализации указателя, будет выбрана функция с типом, равным типу указателя.
fn Bar(i32 x) : i32;
fn Bar(f32 x) : f32;
fn Foo()
{
var ( fn(f32 x) : f32 ) fn_ptr = Bar; // Будет выбрана функция "Bar(f32 x) : f32"
var f32 res= fn_ptr(0.0f);
}
Указателю на функцию можно после инициализации присвоить другой указатель.
fn Bar0();
fn Bar1();
fn Bar2();
type fn_ptr_type= fn();
fn Foo()
{
var fn_ptr_type mut fn_ptr= zero_init;
fn_ptr = fn_ptr_type(Bar0);
fn_ptr(); // Будет вызвана функция "Bar0"
fn_ptr = fn_ptr_type(Bar1);
fn_ptr(); // Будет вызвана функция "Bar1"
}
Существует возможность неявного преобразования функции в указатель, если существует только одна функция с таким именем.
fn Bar();
fn Baz( ( fn() ) ptr );
fn Foo()
{
var ( fn() ) mut ptr= zero_init;
ptr= Bar; // Неявно преобразуем "Bar" в указатель в присваивании.
Baz( Bar ); // Неявно преобразуем "Bar" в указатель при передаче аргумента в функцию.
}
Преобразование указателей на функцию¶
Указатель на функцию можно инициализировать, используя функцию или указатель на функцию с отличным типом от типа указателя. Главное, чтобы тип можно было преобразовать.
Правила преобразования следующие:
- Типы возвращаемого значения должны совпадать
- Ссылочность возвращаемого значения должна совпадать
- Разрешено преобразование изменяемости возвращаемого значения с
mut
наimut
, но не наоборот - Количество аргументов должно совпадать
- Типы аргументов должны совпадать
- Ссылочность аргументов должна совпадать
- Разрешено преобразование изменяемости аргумента с
imut
наmut
, но не наоборот - Разрешено преобразовывать указатель на функцию, возвращающую ссылки на меньшее число аргументов, в указатель на функцию, возвращающую ссылки на большее число аргументов
- Разрешено преборазовывать указатель на функцию, связывающую меньше ссылок, в указатель на функцию, связывающую больше ссылок
- Разрешено преобразовывать указатель на безопасную функцию в указатель на небезопасную (
unsafe
) функцию
fn IMutArgFn( i32 &imut x );
var ( fn( i32 &mut x ) ) mut_arg_fn_ptr = IMutArgFn; // Преобразуется изменяемость аргумента
fn MutRetFn( f32 &mut x ) : f32 &mut;
var ( fn( f32 &mut x ) : f32 &imut ) imut_ret_fn_ptr = MutRetFn; // Преобразуется изменяемость возвращаемого значения
fn SafeFn();
var ( fn() unsafe ) unsafe_fn_ptr = SafeFn; // Преобразуется модификатор небезопасности
var [ [ char8, 2 ], 1 ] return_references_first[ "0_" ];
fn FirstRetFn( i32& x, i32& y ) : i32 & @(return_references_first);
var [ [ char8, 2 ], 2 ] return_references_first_and_second[ "0_", "1_" ];
var ( fn( i32& x, i32& y ) : i32 & @(return_references_first_and_second ) ) all_ret_fn_ptr = FirstRetFn; // Преобразуется модификатор возвращаемых ссылок
При инициализации указателя на функцию с преобразованием компилятор проверяет, что возможно только одно преобразование. При возможности нескольких преобразований компилятор породит ошибку.
fn Foo( i32 &imut x, i32 &mut y );
fn Foo( i32 &mut x, i32 &imut y );
var ( fn( i32 &mut x, i32 &mut y ) ) foo_ptr = Foo; // Ошибка: не возможно выбрать подходящую функцию - слишком много кандидатов
Сравнение указателей на функцию¶
Указатели на функцию можно сравнивать на равенство, но с осторожностью. Гарантируется, что указатели на функцию производные от одного указателя, равны друг другу. Не гарантируется, что равны друг другу указатели на функцию, инициализированные функцией в разных точках программы. Не гарантируются, что не будут равны указатели на функцию, инициализированные разными функциями.
fn Bar0(){}
fn Bar1(){}
fn Foo()
{
var (fn()) ptr0= Bar0;
var (fn()) ptr1 = ptr0;
var (fn()) ptr2= Bar0;
var (fn()) ptr3= Bar1;
auto cmp0 = ptr0 == ptr1; // Гарантируется равенство
auto cmp1 = ptr0 != ptr1; // Гарантируется неравенство
auto cmp2 = ptr0 == ptr2; // Результат может быть как "true", так и "false"
auto cmp3 = ptr3 == ptr0; // Результат может быть как "true", так и "false"
}