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