vtable_gen 0.1.7

A C++-ABI VTable Generator for Rust
Documentation
use vtable_gen::{gen_vtable, new_with_vtable, DefaultVTable};

#[derive(DefaultVTable)]
#[gen_vtable]
struct Foo {
    foo: u32,
}

#[gen_vtable]
struct FooVTable {
    foo: fn(this: &Foo) -> u32,
}

impl FooVirtuals for Foo {
    extern "C" fn foo(_: &Foo) -> u32 {
        0
    }
}

#[derive(DefaultVTable)]
#[gen_vtable(base = "Foo")]
struct Bar {
    bar: u32,
}

impl Bar {
    #[new_with_vtable(base = "Foo")]
    fn new(bar: u32) -> Bar {
        Self { bar }
    }
}

#[gen_vtable(base = "Foo")]
struct BarVTable {
    bar: extern "C" fn(this: &Foo) -> u32,
}

impl BarVirtuals for Bar {
    extern "C" fn bar(_: &Foo) -> u32 {
        2
    }
}

#[gen_vtable(base = "Bar")]
struct Baz {
    baz: u32,
}

impl Baz {
    #[allow(clippy::disallowed_names)]
    #[new_with_vtable(base = "Bar")]
    fn new(baz: u32) -> Baz {
        Self {
            base_with_vtbl: Bar::new(23),
            baz,
        }
    }
}

#[gen_vtable(base = "Bar", no_base_trait_impl)]
struct BazVTable {
    baz: extern "C" fn(this: &Foo) -> u32,
}

impl FooVirtuals for Baz {
    extern "C" fn foo(_: &Foo) -> u32 {
        1
    }
}

impl BarVirtuals for Baz {
    extern "C" fn bar(_: &Foo) -> u32 {
        3
    }
}

impl BazVirtuals for Baz {
    extern "C" fn baz(_: &Foo) -> u32 {
        4
    }
}

#[test]
fn layout() {
    assert_eq!(std::mem::size_of::<Foo>(), std::mem::size_of::<usize>() * 2);
    assert_eq!(
        std::mem::size_of::<FooVTable>(),
        std::mem::size_of::<usize>()
    );
    assert_eq!(std::mem::size_of::<Bar>(), std::mem::size_of::<usize>() * 3);
    assert_eq!(
        std::mem::size_of::<BarVTable>(),
        std::mem::size_of::<usize>() * 2
    );
    assert_eq!(std::mem::size_of::<Baz>(), std::mem::size_of::<usize>() * 4);
    assert_eq!(
        std::mem::size_of::<BazVTable>(),
        std::mem::size_of::<usize>() * 3
    );
}

#[test]
fn basic_foo() {
    let f = Foo::default();

    assert_eq!(unsafe { *(f.vtbl as *const fn(&Foo) -> u32) }(&f), 0);
}

#[test]
fn basic_bar() {
    let b = Bar::default();

    assert_eq!(
        unsafe { *(b.base_with_vtbl.vtbl as *const fn(&Foo) -> u32) }(&b.base_with_vtbl),
        0
    );
    assert_eq!(
        unsafe { *(b.base_with_vtbl.vtbl as *const fn(&Foo) -> u32).add(1) }(&b.base_with_vtbl),
        2
    );
}

#[test]
fn basic_baz() {
    let b = Baz::new(5);

    assert_eq!(
        unsafe { *(b.base_with_vtbl.base_with_vtbl.vtbl as *const fn(&Foo) -> u32) }(
            &b.base_with_vtbl.base_with_vtbl
        ),
        1
    );
    assert_eq!(
        unsafe { *(b.base_with_vtbl.base_with_vtbl.vtbl as *const fn(&Foo) -> u32).add(1) }(
            &b.base_with_vtbl.base_with_vtbl
        ),
        3
    );
    assert_eq!(
        unsafe { *(b.base_with_vtbl.base_with_vtbl.vtbl as *const fn(&Foo) -> u32).add(2) }(
            &b.base_with_vtbl.base_with_vtbl
        ),
        4
    );
}