Last active
August 29, 2015 13:57
-
-
Save dobkeratops/9841737 to your computer and use it in GitHub Desktop.
internal vtable / c++ class emulation , again
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[feature(macro_rules)]; | |
#[allow(macro_rules)]; | |
use std::{io,mem,raw,cast}; | |
// emulating C++ classes using Traits as vtables, could the safety be improved? | |
pub trait Foo { | |
fn foo(&self,i:int); | |
} | |
pub struct CppClass<RawDataStruct,TraitInterface> { | |
priv vtable_ptr: *TraitInterface, // you must not change this :( | |
data:RawDataStruct | |
} | |
type Interface<T> =CppClass<(), T>; | |
impl<Struct,Trait> CppClass<Struct,Trait> { | |
pub fn as_trait_obj<'a>(&'a self)->Trait { | |
unsafe { | |
cast::transmute::<_,Trait>( (self.vtable_ptr, &self.data) ) | |
} | |
} | |
} | |
impl<Struct,Trait> Deref<Trait> for CppClass<Struct,Trait> { | |
fn deref<'a>(&'a self)->&'a Trait { | |
unsafe { | |
// the temporary 'trait object' lives long enough within a | |
// deref expression - its just pointers from the object | |
// its' formed from | |
cast::transmute(&self.as_trait_obj()) | |
} | |
} | |
} | |
macro_rules! new_class { | |
($tname:ty for $sname:ident { $($field:ident : $val:expr),* })=>{ | |
// todo: we can't make initializer list work for arbitrary type, | |
// so add another rule to accept a constructor function for non ident type. | |
CppClass::<$sname,$tname> { | |
vtable_ptr:unsafe { | |
let fake_obj:&'static $sname=cast::transmute(0); | |
let (vt,_):(*u8,*u8)= | |
cast::transmute(fake_obj as &'static $tname); | |
cast::transmute(vt) | |
}, | |
data: $sname { $($field: $val),* } | |
} | |
} | |
} | |
struct Banana { x:int } | |
impl Foo for Banana { | |
fn foo(&self,i:int) { | |
println!("hello from banana.foo(i)={:?} self.x={:?} i+self.x={:?}",i, self.x, i+self.x); | |
} | |
} | |
fn main() { | |
let mut b:CppClass<Banana,&'static Foo> = new_class!( Foo for Banana {x:10} ); | |
b.data.x=20; | |
b.foo(3); | |
// convince ourselves we really do have a compact internal vtable.. | |
println!("size of CppClass={}",mem::size_of_val(&b)); | |
println!("size of data only={}",mem::size_of_val(&b.data)); | |
println!("size of vtable ptr={}",mem::size_of_val(&b.vtable_ptr)); | |
println!("size of any ptr={}",mem::size_of_val(&(&b))); | |
do_with_foo(unsafe{cast::transmute(&b)}); | |
} | |
// Take an object with Foo interface but no knowledge of underlying type. | |
type IFoo=CppClass<(),&'static Foo>; | |
fn do_with_foo(f: &Interface<&'static Foo>) { | |
f.foo(4); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
many variations possible...
etc.
Imagine a typesafe language extention to extract the raw vtable (knowing what struct/impl it is), and recompose it into a temporary fatpointer. Implement various variations of object systems, eg compact vtable lookup from a u8 discriminant .. class-objects holding many instances.. whatever.