use vtable_gen::cpp_class; cpp_class! { #[derive(Debug, Default)] #[gen_vtable(no_unimpl)] struct Foo { a: T, virtual(1) extern "fastcall" fn func(&self, a: u32, b: f32) -> usize, } impl Foo { /// Creates a new `Foo`. fn new(a: T) -> Self { Self { a } } } } impl FooVirtuals for Foo { extern "fastcall" fn func(_this: &Foo, a: u32, b: f32) -> usize { a as usize + b as usize } } cpp_class! { #[derive(Debug, Default)] #[gen_vtable(no_unimpl)] struct Bar: Foo { b: U virtual fn bar(&self) -> &U } impl Bar { fn new(a: U, b: U) -> Self { Self { base_foo: Foo::new(a), b } } } } impl FooVirtuals for Bar { extern "fastcall" fn func(_this: &Foo, a: u32, b: f32) -> usize { a as usize + b as usize + O as usize } } impl BarVirtuals for Bar { extern "C" fn bar(this: &Bar) -> &U { &this.a } } cpp_class! { #[derive(Debug, Default)] #[gen_vtable(no_unimpl)] struct Baz: Bar { c: T virtual fn baz(&self) -> u32 } impl Baz { fn new(a: T, b: T, c: T) -> Self { Self { base_bar: Bar::new(a, b), c } } } } impl FooVirtuals<19, T> for Baz { extern "fastcall" fn func(_this: &Foo<19, T>, a: u32, b: f32) -> usize { a as usize + b as usize + 2 } } impl BarVirtuals for Baz { extern "C" fn bar(this: &Bar) -> &T { &this.b } } impl BazVirtuals for Baz { extern "C" fn baz(_this: &Baz) -> u32 { 123 } } #[test] fn layout() { assert_eq!( std::mem::size_of::>(), std::mem::size_of::() * 4 ); assert_eq!( std::mem::size_of::>(), std::mem::size_of::() * 4 ); } #[test] fn basic() { let b = Baz::::new(1, 2, 3); // manually select the implementation assert_eq!( as FooVirtuals<19, u32>>::func(&b, 1, 2.0), 3); assert_eq!( as FooVirtuals<19, u32>>::func(&b, 1, 2.0), 22); assert_eq!( as FooVirtuals<19, u32>>::func(&b, 1, 2.0), 5); assert_eq!( as BarVirtuals>::bar(&b), &1); assert_eq!( as BarVirtuals>::bar(&b), &2); assert_eq!( as BazVirtuals>::baz(&b), 123); // call through the vtable assert_eq!(b.func(1, 2.0), 5); assert_eq!(b.bar(), &2); assert_eq!(b.baz(), 123); } #[test] #[should_panic] fn unimpl_method() { let b = Baz::::new(1, 2, 3); // ensure that unimplemented methods panic (unsafe { &*(b.vfptr as *const FooVTable<19, u32>) }.unimpl_0)() } #[test] fn default() { let b = Baz::default(); // manually select the implementation assert_eq!( as FooVirtuals<19, u32>>::func(&b, 1, 2.0), 3); assert_eq!( as FooVirtuals<19, u32>>::func(&b, 1, 2.0), 22); assert_eq!( as FooVirtuals<19, u32>>::func(&b, 1, 2.0), 5); assert_eq!( as BarVirtuals>::bar(&b), &0); assert_eq!( as BarVirtuals>::bar(&b), &0); assert_eq!( as BazVirtuals>::baz(&b), 123); // call through the vtable assert_eq!(b.func(1, 2.0), 5); assert_eq!(b.bar(), &0); assert_eq!(b.baz(), 123); }