[][src]Crate ability

ability (ABI compatibility) is a crate for ABI compatibility via traits. The intended use is to allow application developers to specify plugin/extension APIs using traits.

Usage

Add the #[interface] attribute to a trait you wwant to

use ability::interface;

#[interface]
trait Trait {
    fn foo(&self);
    fn bar(&mut self);
    fn baz();
}

In downstream crates you can add the same attribute to structs that implement the trait, marking which traits it implements

#[interface(Trait)]
struct Impl{}
impl Trait for Impl {
    fn foo(&self) {/*...*/}
    fn bar(&mut self) {/*...*/}
    fn baz() {/*...*/}
}

How it works

The above code generates a virtual method table (vtable), which looks like this:

#[allow(non_snake_case)]
#[allow(dead_code )]
pub mod ability_Trait {
    use super::Trait;

    pub extern fn foo <T : Trait> (self_ : * const std::os::raw::c_void) {
        unsafe { (*(self_ as *const T)).foo() }
    }
    pub extern fn bar <T : Trait> (self_ : * mut std::os::raw::c_void) {
        unsafe { (*(self_ as *mut T)).bar() }
    }
    pub extern fn baz <T : Trait>() {
        unsafe { T :: baz() }
     }

    #[repr(C)]
    pub struct
    TraitVTable {
        foo : extern fn (self_ : *const std::os::raw::c_void),
        bar : extern fn (self_ : *mut std::os::raw::c_void),
        baz : extern fn () ,
    }

    impl TraitVTable {
        pub fn new <T : Trait> () -> Self {
            Self {
                foo : foo::<T>,
                bar : bar::<T>,
                baz : baz::<T>,
            }
        }
    }
}

Generated code is placed in a new module which imports the wrapped trait. FFI methods (extern) methods are declared that are generic over types that implement the wrapped trait.

The vtable is generic over types that implement the wrapped trait, and a constructor method is provided to generate the vtable at runtime.

This is similar to how dynamic dispatch works "under the hood." This does not seek to replace dynamic dispatch, just provide functionality across library boundaries that are compiled with different compiler versions (e.g., plugins and application extensions distributed as shared libraries).

When a struct implements the trait, the library will create a single entry point for loading the vtable, which has the signature

#[no_mangle]
extern fn get_ability (trait_ : *const c_char, vtable : *mut c_void);

trait_ is a null terminated C-string that needs to match the wrapped trait's identifier. vtable should be a mutable pointer to an uninitialized instance of the vtable you wish to construct.

On going work

  • Limiting types that can pass across interface boundaries, to prevent subtle bugs from attempting to use incompatible types.
  • A sensible API for wrapping structs that implement the traits
  • Generator/factory pattern to create a single entry point into a shared library
  • A safer interface that doesn't rely on uninitialized memory and raw pointers.

Attribute Macros

interface