Last active
August 29, 2015 14:07
-
-
Save mzabaluev/02202b4a8819687094be to your computer and use it in GitHub Desktop.
Reflecting inheritance and dynamic casting from foreign library types
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(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