Указатели на функцию

В Ü есть возможность сохранить адрес функции в указателе, после чего вызвать её с его использованием.

Указатель на функцию есть отдельный вид типа. Имя типа указателя на функцию начинается с ключевого слова 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"
}

Преобразование указателей на функцию

Указатель на функцию можно инициализировать, используя функцию или указатель на функцию с отличным типом от типа указателя. Главное, чтобы тип можно было преобразовать.

Правила преобразования следующие:

  • Типы возвращаемого значения должны совпадать
  • Ссылочность возвращаемого значения должна совпадать
  • Разрешено преобразование изменяемости возвращаемого значения с 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;  // Преобразуется модификатор небезопасности

fn FirstRetFn( i32 &'first x, i32 &'second y ) : i32 &'first;
var ( fn( i32 &'ret x, i32 &'ret y ) : i32 &'ret ) 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"
}