#![doc = include_str!("../README.md")]
#![no_std]
use alloc::{alloc::dealloc, boxed::Box};
use core::{alloc::Layout, marker::PhantomData, mem::ManuallyDrop, ptr::NonNull};
extern crate alloc;
pub unsafe trait VtableFor<T>: Copy + 'static {
const VTABLE: &'static Self;
}
#[repr(C)]
pub struct RefDyn<'a, Vtable: 'static> {
ptr: NonNull<()>,
vtable: &'static Vtable,
_ref: PhantomData<&'a ()>,
}
impl<'a, Vtable: 'static> RefDyn<'a, Vtable> {
pub const fn new<T>(value: &'a T) -> Self
where
Vtable: VtableFor<T>,
{
Self {
ptr: NonNull::new(value as *const _ as _).unwrap(),
vtable: Vtable::VTABLE,
_ref: PhantomData,
}
}
pub const unsafe fn from_ptr_vtable_unchecked(
ptr: NonNull<()>,
vtable: &'static Vtable,
) -> Self {
Self {
ptr,
vtable,
_ref: PhantomData,
}
}
pub const unsafe fn get_unchecked<T>(&self) -> &T {
unsafe { self.ptr.cast().as_ref() }
}
pub const unsafe fn get_mut_unchecked<T>(&mut self) -> &mut T {
unsafe { self.ptr.cast().as_mut() }
}
pub const fn vtable(&self) -> &'static Vtable {
self.vtable
}
pub const fn as_ptr(&self) -> NonNull<()> {
self.ptr
}
}
#[repr(C)]
pub struct OwnDyn<Vtable: 'static> {
ptr: NonNull<()>,
vtable: &'static Vtable,
}
impl<Vtable: 'static> OwnDyn<Vtable> {
pub fn new<T>(value: Box<T>) -> Self
where
Vtable: VtableFor<T>,
{
Self {
ptr: NonNull::new(Box::into_raw(value).cast()).unwrap(),
vtable: Vtable::VTABLE,
}
}
pub const unsafe fn from_ptr_vtable_unchecked(
ptr: NonNull<()>,
vtable: &'static Vtable,
) -> Self {
Self { ptr, vtable }
}
pub unsafe fn into_box<T>(self) -> Box<T> {
let this = ManuallyDrop::new(self);
unsafe { Box::from_raw(this.ptr.as_ptr().cast()) }
}
pub const unsafe fn get_unchecked<T>(&self) -> &T {
unsafe { self.ptr.cast().as_ref() }
}
pub const unsafe fn get_mut_unchecked<T>(&mut self) -> &mut T {
unsafe { self.ptr.cast().as_mut() }
}
pub const fn vtable(&self) -> &'static Vtable {
self.vtable
}
pub const fn as_ptr(&self) -> NonNull<()> {
self.ptr
}
}
impl<Vtable: 'static> Drop for OwnDyn<Vtable> {
fn drop(&mut self) {
unsafe {
let drop_vtable = &*(&raw const self.vtable as *const DropVtable);
(drop_vtable.drop)(self.ptr);
dealloc(
self.ptr.as_ptr().cast(),
Layout::from_size_align_unchecked(drop_vtable.size, drop_vtable.align),
);
}
}
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct DropVtable {
pub size: usize,
pub align: usize,
pub drop: unsafe extern "C" fn(NonNull<()>),
}
unsafe impl<T> VtableFor<T> for DropVtable {
const VTABLE: &'static Self = {
unsafe extern "C" fn drop_thunk<T>(ptr: NonNull<()>) {
unsafe { ptr.cast::<T>().drop_in_place() }
}
&Self {
size: size_of::<T>(),
align: align_of::<T>(),
drop: drop_thunk::<T>,
}
};
}
#[doc(hidden)]
pub mod __macro_helper {
pub use core::ptr::NonNull;
}
#[macro_export]
macro_rules! vtable_for_trait {
{
$trait_vis:vis trait $trait_name:ident {
$(fn $method_name:ident(& $(mut $(@$self_mut:tt)?)? self $(, $param_name:ident : $param_type:ty)* $(,)?) $(-> $return_type:ty)?;)*
}
$vtable_vis:vis vtable $vtable_name:ident;
} => {
$trait_vis trait $trait_name {
$(fn $method_name(& $(mut $(@$self_mut)?)? self $(, $param_name : $param_type)*) $(-> $return_type)?;)*
}
#[derive(Clone, Copy)]
#[repr(C)]
$vtable_vis struct $vtable_name {
pub drop_vtable: $crate::DropVtable,
$(pub $method_name: unsafe extern "C" fn($crate::__macro_helper::NonNull<()> $(, $param_type)*) $(-> $return_type)?,)*
}
unsafe impl<__T: $trait_name> $crate::VtableFor<__T> for $vtable_name {
const VTABLE: &'static Self = {
$(
unsafe extern "C" fn $method_name<__T: $trait_name>(__ptr: $crate::__macro_helper::NonNull<()> $(, $param_name : $param_type)*) $(-> $return_type)? {
let __ref = unsafe { & $(mut $(@$self_mut)?)? *__ptr.cast::<__T>().as_ptr() };
<__T as $trait_name>::$method_name(__ref $(,$param_name)*)
}
)*
&Self {
drop_vtable: *<$crate::DropVtable as $crate::VtableFor<__T>>::VTABLE,
$(
$method_name: $method_name::<__T>,
)*
}
};
}
impl<'a> $trait_name for $crate::RefDyn<'a, $vtable_name> {
$(
fn $method_name(& $(mut $(@$self_mut)?)? self $(, $param_name : $param_type)*) $(-> $return_type)? {
unsafe { (self.vtable().$method_name)(self.as_ptr() $(, $param_name)*) }
}
)*
}
impl<'a> $trait_name for $crate::OwnDyn<$vtable_name> {
$(
fn $method_name(& $(mut $(@$self_mut)?)? self $(, $param_name : $param_type)*) $(-> $return_type)? {
unsafe { (self.vtable().$method_name)(self.as_ptr() $(, $param_name)*) }
}
)*
}
};
}
#[cfg(test)]
mod tests {
use crate::RefDyn;
vtable_for_trait! {
trait Bar {
fn foo(&self, x: i32) -> f32;
fn bar(&mut self);
}
vtable BarVtable;
}
#[test]
fn bar() {
struct Test;
impl Bar for Test {
fn foo(&self, x: i32) -> f32 {
x as f32
}
fn bar(&mut self) {}
}
let t = Test;
let bar = RefDyn::<BarVtable>::new(&t);
assert_eq!(bar.foo(5), 5.0);
}
}