use std::marker::PhantomData;
use std::cmp::{Eq, PartialEq, Ord, PartialOrd, Ordering};
#[macro_export]
macro_rules! field_ref_of {
($t:ty $(=> $f:tt)+) => {
unsafe {
let base = ::std::ptr::null() as *const $t;
$crate::FieldRef::from_pointers(base, &(*base)$(.$f)+)
}
};
}
pub struct FieldRef<T, U> {
offset: usize,
phantom: PhantomData<(T, U)>,
}
impl<T, U> FieldRef<T, U> {
pub unsafe fn from_offset(offset: usize) -> Self {
Self { offset, phantom: PhantomData }
}
pub unsafe fn from_pointers(obj: *const T, field: *const U) -> Self {
Self::from_offset(field as usize - obj as usize)
}
pub unsafe fn from_references(obj: &T, field: &U) -> Self {
Self::from_pointers(obj, field)
}
pub fn get_ref<'a, 'b>(&'a self, obj: &'b T) -> &'b U {
let addr = obj as *const _ as usize + self.offset;
unsafe { &*(addr as *const U) }
}
pub fn get_mut<'a, 'b>(self, obj: &'b mut T) -> &'b mut U {
let addr = obj as *mut _ as usize + self.offset;
unsafe { &mut *(addr as *mut U) }
}
pub fn chain<V>(&self, fr: FieldRef<U, V>) -> FieldRef<T, V> {
unsafe { FieldRef::<T, V>::from_offset(self.offset + fr.offset) }
}
}
impl<T, U> Clone for FieldRef<T, U> {
fn clone(&self) -> Self {
Self { offset: self.offset, phantom: PhantomData }
}
}
impl<T, U> Copy for FieldRef<T, U> {}
impl<T, U> std::fmt::Debug for FieldRef<T, U> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
write!(f, "FieldRef {{ offset: {} }}", self.offset)
}
}
impl<T, U> PartialEq for FieldRef<T, U> {
fn eq(&self, other: &Self) -> bool {
self.offset == other.offset
}
}
impl<T, U> Eq for FieldRef<T, U> {}
impl<T, U> PartialOrd for FieldRef<T, U> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.offset.partial_cmp(&other.offset)
}
}
impl<T, U> Ord for FieldRef<T, U> {
fn cmp(&self, other: &Self) -> Ordering {
self.offset.cmp(&other.offset)
}
}
pub trait Field where Self: Sized {
fn field<T>(&self, fr: FieldRef<Self, T>) -> &T;
}
pub trait FieldMut where Self: Sized {
fn field_mut<T>(&mut self, fr: FieldRef<Self, T>) -> &mut T;
}
impl<S: Sized> Field for S {
fn field<T>(&self, fr: FieldRef<Self, T>) -> &T {
fr.get_ref(self)
}
}
impl<S: Sized> FieldMut for S {
fn field_mut<T>(&mut self, fr: FieldRef<Self, T>) -> &mut T {
fr.get_mut(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
struct Foo(u32, u32);
struct Bar {
foo: Foo,
x: u32,
}
#[test]
fn basic_test() {
let mut foo = Foo(10, 20);
let fr1 = field_ref_of!(Foo => 0);
let fr2 = field_ref_of!(Foo => 1);
assert_eq!(fr1.get_ref(&foo), &10);
assert_eq!(foo.field(fr2), &20);
*fr2.get_mut(&mut foo) = 30;
*foo.field_mut(fr1) = 40;
assert_eq!(foo.0, 40);
assert_eq!(foo.1, 30);
}
#[test]
fn multi_level_test() {
let bar = Bar{ foo: Foo(10, 20), x: 30 };
let fr1 = field_ref_of!(Bar => foo => 1);
let fr2 = field_ref_of!(Bar => foo);
let fr3 = field_ref_of!(Foo => 1);
let fr4 = field_ref_of!(Bar => x);
assert_eq!(bar.field(fr1), &20);
assert_eq!(bar.field(fr2.chain(fr3)), &20);
assert_eq!(bar.field(fr4), &30);
}
#[test]
fn debug_test() {
let fr = unsafe { FieldRef::<Foo, u32>::from_offset(100) };
assert_eq!(format!("{:?}", fr), "FieldRef { offset: 100 }");
}
#[test]
fn eq_test() {
let fr1 = field_ref_of!(Bar => foo => 1);
let fr2 = field_ref_of!(Bar => foo);
let fr3 = field_ref_of!(Foo => 1);
let fr4 = field_ref_of!(Bar => x);
assert!(fr1 != fr4);
assert!(fr1 == fr2.chain(fr3));
}
#[test]
fn ord_test() {
let fr1 = field_ref_of!(Bar => foo => 0);
let fr2 = field_ref_of!(Bar => foo => 1);
let fr3 = field_ref_of!(Bar => foo);
let fr4 = field_ref_of!(Foo => 1);
assert_eq!(fr1.cmp(&fr2), Ordering::Less);
assert_eq!(fr2.cmp(&fr1), Ordering::Greater);
assert_eq!(fr2.cmp(&fr3.chain(fr4)), Ordering::Equal);
}
}