Skip to content

Instantly share code, notes, and snippets.

@mzabaluev
Last active August 29, 2015 14:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mzabaluev/02202b4a8819687094be to your computer and use it in GitHub Desktop.
Save mzabaluev/02202b4a8819687094be to your computer and use it in GitHub Desktop.
Reflecting inheritance and dynamic casting from foreign library types
#![feature(associated_types)]
extern crate libc;
use libc::c_void;
use std::mem::transmute;
use std::ops::{Deref, DerefMut};
use mod_a::{A, AsA};
use mod_b::B;
unsafe trait Object {
fn type_id(_fixme_ufcs: Option<&Self>) -> i32;
}
trait Upcast<T> {
fn upcast(&self) -> &T;
fn upcast_mut(&mut self) -> &mut T;
}
impl<T> Upcast<T> for T {
fn upcast(&self) -> &T { self }
fn upcast_mut(&mut self) -> &mut T { self }
}
mod mod_a {
use super::{Object, Upcast};
#[repr(C)]
pub struct A;
extern {
fn raw_type_a() -> i32;
fn raw_call_a(p: *mut A);
}
impl A {
pub fn a(&mut self) {
unsafe { raw_call_a(self) };
}
}
unsafe impl Object for A {
fn type_id(_fixme_ufcs: Option<&Self>) -> i32 {
unsafe { raw_type_a() }
}
}
pub trait AsA {
fn as_a(&self) -> &A;
fn as_mut_a(&mut self) -> &mut A;
}
impl<T> AsA for T where T: Upcast<A> {
fn as_a(&self) -> &A { self.upcast() }
fn as_mut_a(&mut self) -> &mut A { self.upcast_mut() }
}
}
mod mod_b {
use super::{Object, Upcast};
use super::mod_a::{A, AsA};
#[repr(C)]
pub struct B {
base: A
}
extern {
fn raw_type_b() -> i32;
fn raw_call_b(p: *const B);
pub fn raw_new_b() -> *mut B;
}
impl B {
pub fn b(&self) {
unsafe { raw_call_b(self) };
}
}
unsafe impl Object for B {
fn type_id(_fixme_ufcs: Option<&Self>) -> i32 {
unsafe { raw_type_b() }
}
}
pub trait AsB : AsA {
fn as_b(&self) -> &B;
fn as_mut_b(&mut self) -> &mut B;
}
impl<T> AsB for T where T: Upcast<B> + Upcast<A> {
fn as_b(&self) -> &B { self.upcast() }
fn as_mut_b(&mut self) -> &mut B { self.upcast_mut() }
}
impl Upcast<A> for B {
fn upcast(&self) -> &A { &self.base }
fn upcast_mut(&mut self) -> &mut A { &mut self.base }
}
}
extern {
fn raw_type_check(p: *const c_void, type_id: i32) -> bool;
}
enum CastError {
NotType(i32)
}
fn type_of<T>() -> i32
where T: Object
{
let fixme_ufcs: Option<&T> = None;
Object::type_id(fixme_ufcs)
}
fn cast<T, U>(source: &T) -> &U
where T: Object, U: Object
{
unsafe {
let ps = source as *const T as *const c_void;
if !raw_type_check(ps, type_of::<U>()) {
panic!("Type check failed");
}
transmute(source)
}
}
fn try_cast<T, U>(source: &T) -> Result<&U, CastError>
where T: Object, U: Object
{
unsafe {
let ps = source as *const T as *const c_void;
let dest_type = type_of::<U>();
if raw_type_check(ps, dest_type) {
Ok(transmute(source))
} else {
Err(CastError::NotType(dest_type))
}
}
}
struct R<T> {
ptr: *mut T
}
impl<T> Deref for R<T> {
type Target = T;
fn deref(&self) -> &T { unsafe { &*self.ptr } }
}
impl<T> DerefMut for R<T> {
fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.ptr } }
}
fn work_with_b_passed_as_a(a: &A) {
let bc: &B = cast(a);
bc.b();
let bc: &B = try_cast(a).ok().unwrap();
bc.b();
}
fn main() {
let mut rb: R<B> = unsafe { R { ptr: mod_b::raw_new_b() } };
rb.as_mut_a().a();
work_with_b_passed_as_a(rb.as_a());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment