use cpp_oop::gen_oop; use cpp_oop::CastPtr; gen_oop! { #[default] struct Foo { foo: u32, } struct FooLayout { #[virt] foo: fn(this: &Self, a: i32) -> u32, #[virt] foo2: fn(this: &Self) -> u32, } impl FooMethodsInner for Foo { extern "C" fn foo_inner_virt(_this: &Self, _a: i32) -> u32 { 1 } extern "C" fn foo2_inner_virt(_this: &Self) -> u32 { 2 } } } gen_oop! { #[default] #[base(Foo)] struct Bar { bar: u32, } struct BarLayout { bar: fn(this: &mut Self) -> u32, bar2: fn(this: &Self) -> u32, } impl BarMethodsInner for Bar { extern "C" fn bar_inner(_this: &mut Self) -> u32 { 3 } extern "C" fn bar2_inner(_this: &Self) -> u32 { 4 } } impl FooMethodsInner for Bar { extern "C" fn foo_inner_virt(_this: &Self, _a: i32) -> u32 { 10 } #[unused] extern "C" fn foo2_inner_virt(_this: &Self) -> u32 {} } } gen_oop! { #[default] #[base(Bar)] struct Baz { baz: u32, } struct BazLayout { #[virt] baz: fn(this: &Self) -> u32, } impl BazMethodsInner for Baz { extern "C" fn baz_inner_virt(_this: &Self) -> u32 { 5 } } impl BarMethodsInner for Baz { extern "C" fn foo_inner_virt(_this: &Self, _a: i32) -> u32 { 100 } #[unused] extern "C" fn foo2_inner_virt(_this: &Self) -> u32 {} extern "C" fn bar_inner(_this: &mut Self) -> u32 { 30 } #[unused] extern "C" fn bar2_inner(_this: &Self) -> u32 {} } } #[test] fn layout() { assert_eq!(std::mem::size_of::(), std::mem::size_of::() * 2); assert_eq!(std::mem::size_of::(), std::mem::size_of::() * 3); assert_eq!(std::mem::size_of::(), std::mem::size_of::() * 4); } #[test] fn basic() { let mut bz = Baz::default(); let mut bz_ptr = &mut bz as *mut Baz; let mut b_ptr: *mut Bar = Bar::cast_mut(bz_ptr); let f_ptr: *const Foo = Foo::cast_const(b_ptr); assert_eq!(Foo::foo_inner_virt(&bz.base_bar.base_foo, 0), 1); // manual selection of the method to call assert_eq!(Bar::foo_inner_virt(&bz.base_bar, 0), 10); // manual selection of the method to call assert_eq!(Baz::foo_inner_virt(&bz, 0), 100); // manual selection of the method to call assert_eq!(Foo::foo2_inner_virt(&bz.base_bar.base_foo), 2); // manual selection of the method to call // Bar::foo2_inner will give an error cuz its unused // Baz::foo2_inner will give an error cuz its unused assert_eq!(Bar::bar_inner(&mut bz.base_bar), 3); // manual selection of the method to call assert_eq!(Baz::bar_inner(&mut bz), 30); // manual selection of the method to call assert_eq!(Bar::bar2_inner(&bz.base_bar), 4); // manual selection of the method to call // Baz::bar2_inner will give an error cuz its unused assert_eq!(Baz::baz_inner_virt(&bz), 5); // manual selection of the method to call assert_eq!(bz.foo(0), 100); // static dispatch assert_eq!(bz.foo2(), 2); // static dispatch assert_eq!(bz.bar(), 30); // static dispatch assert_eq!(bz.bar2(), 4); // static dispatch assert_eq!(bz.baz(), 5); // static dispatch assert_eq!(f_ptr.foo(0), 100); // this one will use vtable assert_eq!(f_ptr.foo2(), 2); // this one will use vtable assert_eq!(b_ptr.foo(0), 100); // this one will use vtable assert_eq!(b_ptr.foo2(), 2); // this one will use vtable assert_eq!(b_ptr.bar(), 3); // this one is no virt so it will use static dispatch assert_eq!(b_ptr.bar2(), 4); // this one is no virt so it will use static dispatch assert_eq!(bz_ptr.foo(0), 100); // this one will use vtable assert_eq!(bz_ptr.foo2(), 2); // this one will use vtable assert_eq!(bz_ptr.bar(), 30); // this one is no virt so it will use static dispatch assert_eq!(bz_ptr.bar2(), 4); // this one is no virt so it will use static dispatch assert_eq!(bz_ptr.baz(), 5); // this one is no virt so it will use static dispatch }