12 releases (2 stable)
1.1.0 | Jul 20, 2024 |
---|---|
1.0.0 | Jun 21, 2024 |
0.3.0 | Dec 6, 2023 |
0.2.1 | Apr 5, 2023 |
0.1.7 | Oct 3, 2022 |
#150 in Procedural macros
65KB
1.5K
SLoC
VTable Gen
This crate provides macros to generate C++-ABI VTables by defining the structure and vtable layout. It also supports VTable inheritance and basic class inheritance.
Examples
Check out tests.rs
, which is pretty self-explanatory.
Usage
Base Structs
- Define a structure that contains virtual functions
- Define a structure for the VTable that exactly matches the name of the structure it belongs to,
followed by
VTable
exactly. Example:
struct Foo {}
struct FooVTable {}
- Mark both the VTable and structure with
#[gen_vtable]
. Any function pointers you include in the VTable struct will require implementation in an automatically-generated<name>Virtuals
trait. Complete Example:
#[gen_vtable]
struct Foo {}
#[gen_vtable]
struct FooVTable {
foo: extern "C" fn(this: &Foo) -> u32;
}
impl FooVirtuals for Foo {
extern "C" fn foo(this: &Foo) -> u32 { todo!() }
}
Derived Structs
- Define structures exactly as with base structures
- Include the attribute
base
. Example:
#[gen_vtable]
struct Foo {}
#[gen_vtable]
struct FooVTable {}
#[gen_vtable(base = "Foo")]
struct Bar {}
#[gen_vtable(base = "Foo")]
struct BarVTable {}
Constructing Structs with VTables
Constructing structs with VTables is easy. If the struct is default-able, simply derive
DefaultVTable
instead of Default
. This will impl Default
. If the struct isn't default-able,
define some function fn new(/* args */) -> Self
. Mark the function with new_with_vtable
,
supplying base structs if necessary as in Derived Structs
. For the compiler to know the type,
you must either explicitly replace Self
as the return type with the type itself, or specify
self_type
. Here's a verbose example:
// ...
impl Bar {
#[new_with_vtable(self_type = "Bar")]
fn new(baz: u32) -> Self {
Self { baz }
}
}
which is also equivalent to
// ...
impl Bar {
#[new_with_vtable]
fn new(baz: u32) -> Bar {
Self { baz }
}
}
If there is a base struct that requires its new
function to be called, you will have to also
explicitly initialize a base_with_vtbl
member with the new
constructor of the child type.
For example:
// ...
impl Bar {
#[new_with_vtable(base = "Foo", self_type = "Bar")]
fn new(baz: u32) -> Self {
Self {
base_with_vtable: Foo::new(123),
baz
}
}
}
Overriding Functions
Overriding functions is easy. Because all functions are defined in Traits, one can specify for the
compiler to not generate implementations for base struct Virtuals
with the argument no_base_trait_impl
on the VTable (or both for symmetry :)).
Example:
// ...
#[gen_vtable(base = "Foo", no_base_trait_impl)]
struct BarVTable {}
// ...
impl FooVirtuals for Bar {
extern "C" fn some_fn(this: &Foo) {
// ...
}
}
The only caveat is you will have to implement all base traits.
Automatic Implementation
For an automatic implementation, in the case of some abstract struct for example, simply supply unimpl
as an argument to gen_vtable
, and all methods will be implemented with unimplemented!()
. Example:
// ...
#[gen_vtable(unimpl)]
struct Foo {}
#[gen_vtable(unimpl)]
struct FooVTable {}
// `FooVirtuals` is implemented for `Foo`
Known Limitations
vtable_gen
currently does not support generic structs. This is a trivial addition, however, and will likely be added in the future
Dependencies
~1.4–2MB
~38K SLoC