fn-ptr 0.9.1

A utility crate for introspecting and rewriting function pointer types at compile time.
Documentation
#![allow(unpredictable_function_pointer_comparisons)]

use fn_ptr::{AbiValue, FnPtr, SafeFnPtr, UnsafeFnPtr, abi, arity, is_extern, is_safe, is_unsafe};

use static_assertions::assert_type_eq_all;

#[test]
fn unsafe_fn() {
    type F = unsafe fn(i32) -> i32;

    assert_type_eq_all!(<F as FnPtr>::Args, (i32,));
    assert_type_eq_all!(<F as FnPtr>::Output, i32);

    assert_eq!(arity::<F>(), 1);
    assert!(is_unsafe::<F>());
    assert!(!is_extern::<F>());
    assert_eq!(abi::<F>(), AbiValue::Rust);
}

#[test]
fn extern_c_fn() {
    type F = extern "C" fn(i32) -> i32;

    assert_type_eq_all!(<F as FnPtr>::Args, (i32,));
    assert_type_eq_all!(<F as FnPtr>::Output, i32);

    assert_eq!(arity::<F>(), 1);
    assert!(is_safe::<F>());
    assert!(is_extern::<F>());
    assert_eq!(abi::<F>(), AbiValue::C { unwind: false });
}

#[test]
fn zero_arg_fn() {
    type F = fn() -> i32;

    assert_type_eq_all!(<F as FnPtr>::Args, ());
    assert_type_eq_all!(<F as FnPtr>::Output, i32);

    assert_eq!(arity::<F>(), 0);
    assert!(is_safe::<F>());
    assert!(!is_extern::<F>());
    assert_eq!(abi::<F>(), AbiValue::Rust);
}

#[test]
fn multi_arg_fn() {
    type F = fn(i32, i32, i32) -> i32;

    assert_type_eq_all!(<F as FnPtr>::Args, (i32, i32, i32));
    assert_type_eq_all!(<F as FnPtr>::Output, i32);

    assert_eq!(arity::<F>(), 3);
    assert!(is_safe::<F>());
    assert!(!is_extern::<F>());
    assert_eq!(abi::<F>(), AbiValue::Rust);
}

#[test]
fn no_ret() {
    type F = fn(i32);

    assert_type_eq_all!(<F as FnPtr>::Args, (i32,));
    assert_type_eq_all!(<F as FnPtr>::Output, ());

    assert_eq!(arity::<F>(), 1);
    assert!(is_safe::<F>());
    assert!(!is_extern::<F>());
    assert_eq!(abi::<F>(), AbiValue::Rust);
}

#[test]
fn addr_and_from_addr() {
    fn add(a: i32, b: i32) -> i32 {
        a + b
    }

    type F = fn(i32, i32) -> i32;
    let f: F = add;

    let addr = f.addr();
    assert_ne!(addr, 0);

    let f2: F = unsafe { F::from_addr(addr) };
    assert_eq!(f2.addr(), addr);
    assert_eq!(f, f2);
}

#[test]
fn as_ptr_and_from_ptr() {
    unsafe fn mul(a: i32, b: i32) -> i32 {
        a * b
    }

    type F = unsafe fn(i32, i32) -> i32;
    let f: F = mul;

    let ptr = f.as_ptr();
    assert!(!ptr.is_null());

    let f2: F = unsafe { F::from_ptr(ptr) };
    assert_eq!(f2.as_ptr(), ptr);
    assert_eq!(f, f2);
}

#[test]
fn invoke_safe_fnptr() {
    fn square(x: i32) -> i32 {
        x * x
    }

    type F = fn(i32) -> i32;
    let f: F = square;

    assert_eq!(f.invoke((5,)), 25);
    assert_eq!(f.invoke((0,)), 0);
}

#[test]
fn invoke_unsafe_fnptr() {
    unsafe fn negate(x: i32) -> i32 {
        -x
    }

    type F = unsafe fn(i32) -> i32;
    let f: F = negate;

    unsafe {
        assert_eq!(f.invoke((10,)), -10);
        assert_eq!(f.invoke((0,)), 0);
    }
}