#[cfg(feature = "dynamic")]
#[macro_use]
extern crate lazy_static;
use std::any::{TypeId, Any};
use std::ptr;
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use std::collections::hash_map::DefaultHasher;
use std::fmt::{Debug, Display};
use std::path::PathBuf;
#[cfg(feature = "dynamic")]
#[macro_use]
pub mod dynamic;
#[doc(hidden)]
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct VTable(*const ());
impl VTable {
pub fn none() -> VTable {
VTable(ptr::null())
}
}
unsafe impl Send for VTable {}
unsafe impl Sync for VTable {}
#[doc(hidden)]
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct TraitObject {
pub data: *const (),
pub vtable: VTable
}
#[doc(hidden)]
#[macro_export]
macro_rules! vtable_for {
($x:ty as $y:ty) => ({
let x = ::std::ptr::null::<$x>() as *const $y;
#[allow(unused_unsafe)]
unsafe { ::std::mem::transmute::<_, $crate::TraitObject>(x).vtable }
})
}
#[macro_export]
macro_rules! mopo {
($name:ty) => (
impl $name {
pub fn query_ref<U: ::std::any::Any + ?Sized>(&self) -> Option<&U> {
if let Some(vtable) = self.query_vtable(::std::any::TypeId::of::<U>()) {
unsafe {
let data = self as *const Self;
let u = $crate::TraitObject { data: data as *const (), vtable: vtable };
Some(*::std::mem::transmute::<_, &&U>(&u))
}
} else {
None
}
}
pub fn query_mut<U: ::std::any::Any + ?Sized>(&mut self) -> Option<&mut U> {
if let Some(vtable) = self.query_vtable(::std::any::TypeId::of::<U>()) {
unsafe {
let data = self as *mut Self;
let mut u = $crate::TraitObject { data: data as *const (), vtable: vtable };
Some(*::std::mem::transmute::<_, &mut &mut U>(&mut u))
}
} else {
None
}
}
pub fn query<U: ::std::any::Any + ?Sized>(self: Box<Self>) -> ::std::result::Result<Box<U>, Box<Self>> {
if let Some(vtable) = self.query_vtable(::std::any::TypeId::of::<U>()) {
unsafe {
let data = Box::into_raw(self);
let mut u = $crate::TraitObject { data: data as *const (), vtable: vtable };
Ok(Box::from_raw(*::std::mem::transmute::<_, &mut *mut U>(&mut u)))
}
} else {
Err(self)
}
}
pub fn query_arc<U: ::std::any::Any + ?Sized>(self_: ::std::sync::Arc<Self>) -> ::std::result::Result<::std::sync::Arc<U>, ::std::sync::Arc<Self>> {
if let Some(vtable) = self_.query_vtable(::std::any::TypeId::of::<U>()) {
unsafe {
let data = ::std::sync::Arc::into_raw(self_);
let mut u = $crate::TraitObject { data: data as *const (), vtable: vtable };
Ok(::std::sync::Arc::from_raw(*::std::mem::transmute::<_, &mut *mut U>(&mut u)))
}
} else {
Err(self_)
}
}
pub fn query_rc<U: ::std::any::Any + ?Sized>(self_: ::std::rc::Rc<Self>) -> ::std::result::Result<::std::rc::Rc<U>, ::std::rc::Rc<Self>> {
if let Some(vtable) = self_.query_vtable(::std::any::TypeId::of::<U>()) {
unsafe {
let data = ::std::rc::Rc::into_raw(self_);
let mut u = $crate::TraitObject { data: data as *const (), vtable: vtable };
Ok(::std::rc::Rc::from_raw(*::std::mem::transmute::<_, &mut *mut U>(&mut u)))
}
} else {
Err(self_)
}
}
pub fn obj_partial_eq(&self, other: &Self) -> bool {
if let Some(x) = self.query_ref::<$crate::ObjectPartialEq>() {
x.obj_eq(other.query_ref().unwrap())
} else {
(self as *const Self) == (other as *const Self)
}
}
pub fn obj_partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> {
if let Some(x) = self.query_ref::<$crate::ObjectPartialOrd>() {
x.obj_partial_cmp(other.query_ref().unwrap())
} else {
None
}
}
}
impl ::std::clone::Clone for Box<$name> {
fn clone(&self) -> Self {
(**self).to_owned()
}
}
impl ::std::borrow::ToOwned for $name {
type Owned = Box<$name>;
fn to_owned(&self) -> Box<$name> {
self.query_ref::<$crate::ObjectClone>().expect("Object not clonable!").obj_clone().query::<$name>().unwrap()
}
}
impl ::std::fmt::Debug for $name {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
if let Some(o) = self.query_ref::<::std::fmt::Debug>() {
o.fmt(f)
} else {
writeln!(f, "Object {{ <no `Debug` implementation> }}")
}
}
}
impl ::std::cmp::PartialEq for $name {
fn eq(&self, other: &Self) -> bool {
if let Some(x) = self.query_ref::<$crate::ObjectEq>() {
x.obj_eq(other.query_ref().unwrap())
} else {
(self as *const Self) == (other as *const Self)
}
}
}
impl ::std::cmp::Eq for $name {}
impl ::std::cmp::PartialOrd for $name {
fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl ::std::cmp::Ord for $name {
fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
if let Some(x) = self.query_ref::<$crate::ObjectOrd>() {
if let Some(o) = x.obj_cmp(other.query_ref().unwrap()) {
return o
}
}
Ord::cmp(&(self as *const Self), &(other as *const Self))
}
}
impl ::std::hash::Hash for $name {
fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
if let Some(x) = self.query_ref::<$crate::ObjectHash>() {
x.obj_hash(state)
} else {
state.write_usize(self as *const Self as *const () as usize)
}
}
}
)
}
pub unsafe trait Object: Any {
#[doc(hidden)]
fn query_vtable(&self, id: TypeId) -> Option<VTable>;
}
pub unsafe trait HasInterface<I: ?Sized> {}
mopo!(Object);
pub trait ObjectClone {
fn obj_clone(&self) -> Box<Object>;
}
impl<T: Clone + Object> ObjectClone for T {
fn obj_clone(&self) -> Box<Object> {
Box::new(self.clone())
}
}
pub trait ObjectPartialEq {
fn obj_eq(&self, other: &Object) -> bool;
}
impl<T: PartialEq + Object> ObjectPartialEq for T {
fn obj_eq(&self, other: &Object) -> bool {
if let Some(o) = other.query_ref::<Self>() {
self == o
} else {
false
}
}
}
pub trait ObjectEq: ObjectPartialEq {}
impl<T: Eq + Object> ObjectEq for T {}
pub trait ObjectPartialOrd {
fn obj_partial_cmp(&self, other: &Object) -> Option<Ordering>;
}
impl<T: PartialOrd + Object> ObjectPartialOrd for T {
fn obj_partial_cmp(&self, other: &Object) -> Option<Ordering> {
if let Some(o) = other.query_ref::<Self>() {
self.partial_cmp(o)
} else {
None
}
}
}
pub trait ObjectOrd {
fn obj_cmp(&self, other: &Object) -> Option<Ordering>;
}
impl<T: Ord + Object> ObjectOrd for T {
fn obj_cmp(&self, other: &Object) -> Option<Ordering> {
if let Some(o) = other.query_ref::<Self>() {
Some(self.cmp(o))
} else {
None
}
}
}
pub trait ObjectHash {
fn obj_hash(&self, state: &mut Hasher);
}
impl<T: Hash + Object> ObjectHash for T {
fn obj_hash(&self, state: &mut Hasher) {
let mut h = DefaultHasher::new();
self.hash(&mut h);
state.write_u64(h.finish());
}
}
#[macro_export]
macro_rules! interfaces {
(@unbracket $(($($v:tt)*))*) => ($($($v)*)*);
(@inner $imp:tt $cond:tt $name:ty: $($iface:ty),+ {}) => (
interfaces!(@unbracket $imp ($crate::HasInterface<$name> for $name) $cond ({}));
interfaces!(@unbracket $imp ($crate::HasInterface<$crate::Object> for $name) $cond ({}));
$(interfaces!(@unbracket $imp ($crate::HasInterface<$iface> for $name) $cond ({}));)*
interfaces!(@unbracket $imp ($crate::Object for $name) $cond ({
fn query_vtable(&self, id: ::std::any::TypeId) -> Option<$crate::VTable> {
if id == ::std::any::TypeId::of::<$name>() {
Some($crate::VTable::none())
} else if id == ::std::any::TypeId::of::<$crate::Object>() {
Some(vtable_for!($name as $crate::Object))
} else $(if id == ::std::any::TypeId::of::<$iface>() {
Some(vtable_for!($name as $iface))
} else)* {
#[cfg(feature = "dynamic")]
{ $crate::dynamic::find_in_registry::<$name>(id) }
#[cfg(not(feature = "dynamic"))]
{ None }
}
}
}));
);
(@imp ($($result:tt)*) $name:ty: $($iface:ty),+ $(where $($cond:tt)*)*) => (
interfaces!(@inner (unsafe impl<$($result)*>) ($(where $($cond)*)*) $name: $($iface),+ {});
);
(@parse < $($rest:tt)*) => (
interfaces!(@parseArg () $($rest)*);
);
(@parse $($rest:tt)*) => (
interfaces!(@imp () $($rest)*);
);
(@parseArg ($($result:tt)*) $name:ident , $($rest:tt)*) => (
interfaces!(@parseArg ($($result)* $name ,) $($rest)*);
);
(@parseArg ($($result:tt)*) $name:ident : $($rest:tt)*) => (
interfaces!(@parseBound ($($result)* $name : ) $($rest)*);
);
(@parseArg ($($result:tt)*) $name:ident > $($rest:tt)*) => (
interfaces!(@imp ($($result)* $name) $($rest)*);
);
(@parseBound ($($result:tt)*) $bound:tt + $($rest:tt)*) => (
interfaces!(@parseBound ($($result)* $bound +) $($rest)*);
);
(@parseBound ($($result:tt)*) $bound:tt , $($rest:tt)*) => (
interfaces!(@parseArg ($($result)* $bound ,) $($rest)*);
);
(@parseBound ($($result:tt)*) $bound:tt > $($rest:tt)*) => (
interfaces!(@imp ($($result)* $bound) $($rest)*);
);
(< $($rest:tt)*) => (
interfaces!(@parse < $($rest)*);
);
($x:ty: $($rest:tt)*) => (
interfaces!(@parse $x: $($rest)*);
);
(@expand2 ($name:ty) ($($rest:tt)*)) => (
interfaces!($name $($rest)*);
);
(@expand {$($name:ty),*} $rest:tt) => (
$( interfaces!(@expand2 ($name) $rest); )*
);
({$($name:ty),*} $($rest:tt)*) => (
interfaces!(@expand {$($name),*} ($($rest)*));
);
}
interfaces!({
bool, i8, u8, i16, u16, i32, u32, i64, u64, char
}: ObjectClone, Debug, Display, ObjectPartialEq, ObjectPartialOrd, ObjectEq, ObjectOrd, ObjectHash, ToString);
interfaces!({
f32, f64
}: ObjectClone, Debug, Display, ObjectPartialEq, ObjectPartialOrd, ToString);
interfaces!(String: ObjectClone, Debug, Display, ObjectPartialEq, ObjectPartialOrd, ObjectEq, ObjectOrd, ObjectHash, ToString);
interfaces!(PathBuf: ObjectClone, Debug, ObjectPartialEq, ObjectPartialOrd, ObjectEq, ObjectOrd, ObjectHash);
interfaces!({
Vec<bool>, Vec<i8>, Vec<u8>, Vec<i16>, Vec<u16>, Vec<i32>, Vec<u32>, Vec<i64>, Vec<u64>, Vec<char>
}: ObjectClone, Debug, ObjectPartialEq, ObjectPartialOrd, ObjectEq, ObjectOrd, ObjectHash);
interfaces!({
Vec<f32>, Vec<f64>
}: ObjectClone, Debug, ObjectPartialEq, ObjectPartialOrd);
interfaces!({
Vec<String>, Vec<PathBuf>
}: ObjectClone, Debug, ObjectPartialEq, ObjectPartialOrd, ObjectEq, ObjectOrd, ObjectHash);
#[cfg(test)]
mod tests {
use std::fmt::Debug;
use std::sync::Arc;
use std::rc::Rc;
#[derive(Debug, Clone)]
struct Bar;
interfaces!(Bar: Foo, super::ObjectClone, Debug, Custom);
trait Foo: Debug {
fn test(&self) -> bool { false }
}
trait Foo2: Debug {}
impl Foo for Bar {
fn test(&self) -> bool { true }
}
impl Foo2 for Bar {}
#[derive(Debug, Clone)]
struct GenericBar<T>(T);
interfaces!(<T: Debug + 'static> GenericBar<T>: super::ObjectClone, Debug where T: Clone);
#[test]
fn test_ref() {
let x = Box::new(Bar) as Box<super::Object>;
let foo: Option<&Foo> = x.query_ref();
assert!(foo.is_some());
assert!(foo.unwrap().test());
let foo2: Option<&Foo2> = x.query_ref();
assert!(foo2.is_none());
let bar: Option<&Bar> = x.query_ref();
assert!(bar.is_some());
}
#[test]
fn test_mut() {
let mut x = Box::new(Bar) as Box<super::Object>;
{
let foo = x.query_mut::<Foo>();
assert!(foo.is_some());
assert!(foo.unwrap().test());
}
{
let foo2 = x.query_mut::<Foo2>();
assert!(foo2.is_none());
}
{
let bar = x.query_mut::<Bar>();
assert!(bar.is_some());
}
}
#[test]
fn test_owned() {
let x = Box::new(Bar) as Box<super::Object>;
let foo: Result<Box<Foo>, _> = x.clone().query();
assert!(foo.is_ok());
assert!(foo.unwrap().test());
let foo2: Result<Box<Foo2>, _> = x.clone().query();
assert!(foo2.is_err());
let bar: Result<Box<Bar>, _> = x.clone().query();
assert!(bar.is_ok());
}
#[test]
fn test_rc() {
let x = Rc::new(Bar) as Rc<super::Object>;
let foo: Result<Rc<Foo>, _> = super::Object::query_rc(x.clone());
assert!(foo.is_ok());
assert!(foo.unwrap().test());
let foo2: Result<Rc<Foo2>, _> = super::Object::query_rc(x.clone());
assert!(foo2.is_err());
let bar: Result<Rc<Bar>, _> = super::Object::query_rc(x.clone());
assert!(bar.is_ok());
}
#[test]
fn test_arc() {
let x = Arc::new(Bar) as Arc<super::Object>;
let foo: Result<Arc<Foo>, _> = super::Object::query_arc(x.clone());
assert!(foo.is_ok());
assert!(foo.unwrap().test());
let foo2: Result<Arc<Foo2>, _> = super::Object::query_arc(x.clone());
assert!(foo2.is_err());
let bar: Result<Arc<Bar>, _> = super::Object::query_arc(x.clone());
assert!(bar.is_ok());
}
trait Custom : super::Object {}
impl Custom for Bar {}
mopo!(Custom);
#[test]
fn test_derived() {
let x = Box::new(Bar) as Box<Custom>;
let foo: Result<Box<Foo>, _> = x.clone().query();
assert!(foo.is_ok());
assert!(foo.unwrap().test());
let foo2: Result<Box<Foo2>, _> = x.clone().query();
assert!(foo2.is_err());
let bar: Result<Box<Bar>, _> = x.clone().query();
assert!(bar.is_ok());
}
trait Dynamic {
fn test(&self) -> u32;
}
impl Dynamic for Bar {
fn test(&self) -> u32 { 42 }
}
#[test]
fn test_dynamic() {
let x = Box::new(Bar) as Box<super::Object>;
let dyn1: Option<&Dynamic> = x.query_ref();
assert!(dyn1.is_none());
dynamic_interfaces! {
Bar: Dynamic;
}
let dyn2: Option<&Dynamic> = x.query_ref();
assert!(dyn2.unwrap().test() == 42);
}
#[test]
fn test_primitives() {
Box::new(1) as Box<super::Object>;
Box::new(1f32) as Box<super::Object>;
Box::new("test".to_string()) as Box<super::Object>;
Box::new(vec![1,2,3]) as Box<super::Object>;
}
}