use super::*;
use extendr_ffi::{
R_ClearExternalPtr, R_ExternalPtrAddr, R_ExternalPtrProtected, R_ExternalPtrTag,
R_MakeExternalPtr, R_NilValue, R_SetExternalPtrTag,
};
use std::{any::Any, fmt::Debug};
#[repr(transparent)]
pub struct ExternalPtr<T> {
pub(crate) robj: Robj,
_marker: std::marker::PhantomData<T>,
}
impl<T> PartialEq for ExternalPtr<T> {
fn eq(&self, other: &Self) -> bool {
self.robj == other.robj && self._marker == other._marker
}
}
impl<T> Clone for ExternalPtr<T> {
fn clone(&self) -> Self {
Self {
robj: self.robj.clone(),
_marker: self._marker,
}
}
}
impl<T> robj::GetSexp for ExternalPtr<T> {
unsafe fn get(&self) -> SEXP {
self.robj.get()
}
unsafe fn get_mut(&mut self) -> SEXP {
self.robj.get_mut()
}
fn as_robj(&self) -> &Robj {
&self.robj
}
fn as_robj_mut(&mut self) -> &mut Robj {
&mut self.robj
}
}
impl<T> Length for ExternalPtr<T> {}
impl<T> Types for ExternalPtr<T> {}
impl<T> Attributes for ExternalPtr<T> {}
impl<T> Conversions for ExternalPtr<T> {}
impl<T> Rinternals for ExternalPtr<T> {}
impl<T> Slices for ExternalPtr<T> {}
impl<T> Operators for ExternalPtr<T> {}
impl<T: 'static> Deref for ExternalPtr<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.addr()
}
}
impl<T: 'static> DerefMut for ExternalPtr<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.addr_mut()
}
}
impl<T: 'static> ExternalPtr<T> {
pub fn new(val: T) -> Self {
single_threaded(|| unsafe {
let boxed: Box<dyn Any> = Box::new(val);
let boxed: Box<Box<dyn Any>> = Box::new(boxed);
let robj = {
let boxed_ptr = Box::into_raw(boxed);
let prot = Robj::from(());
let type_name: Robj = std::any::type_name::<T>().into();
Robj::from_sexp(single_threaded(|| {
R_MakeExternalPtr(boxed_ptr.cast(), type_name.get(), prot.get())
}))
};
extern "C" fn finalizer(x: SEXP) {
unsafe {
let ptr = R_ExternalPtrAddr(x).cast::<Box<dyn Any>>();
R_SetExternalPtrTag(x, R_NilValue);
drop(Box::from_raw(ptr));
R_ClearExternalPtr(x);
}
}
robj.register_c_finalizer(Some(finalizer));
Self {
robj,
_marker: std::marker::PhantomData,
}
})
}
pub fn tag(&self) -> Robj {
unsafe { Robj::from_sexp(R_ExternalPtrTag(self.robj.get())) }
}
pub fn protected(&self) -> Robj {
unsafe { Robj::from_sexp(R_ExternalPtrProtected(self.robj.get())) }
}
pub fn addr(&self) -> &T {
self.try_addr().unwrap()
}
pub fn addr_mut(&mut self) -> &mut T {
self.try_addr_mut().unwrap()
}
pub fn try_addr(&self) -> Result<&T> {
unsafe {
R_ExternalPtrAddr(self.robj.get())
.cast::<Box<dyn Any>>()
.as_ref()
.ok_or_else(|| Error::ExpectedExternalNonNullPtr(self.robj.clone()))
.map(|x| x.downcast_ref::<T>().unwrap())
}
}
pub fn try_addr_mut(&mut self) -> Result<&mut T> {
unsafe {
R_ExternalPtrAddr(self.robj.get_mut())
.cast::<Box<dyn Any>>()
.as_mut()
.ok_or_else(|| Error::ExpectedExternalNonNullPtr(self.robj.clone()))
.map(|x| x.downcast_mut::<T>().unwrap())
}
}
}
impl<T: 'static> TryFrom<&Robj> for &ExternalPtr<T> {
type Error = Error;
fn try_from(value: &Robj) -> Result<Self> {
if !value.is_external_pointer() {
return Err(Error::ExpectedExternalPtr(value.clone()));
}
let boxed_ptr = unsafe {
value
.external_ptr_addr::<Box<dyn Any>>()
.cast_const()
.as_ref()
.ok_or_else(|| Error::ExpectedExternalNonNullPtr(value.clone()))?
};
if boxed_ptr.downcast_ref::<T>().is_none() {
return Err(Error::ExpectedExternalPtrType(
value.clone(),
std::any::type_name::<T>().to_string(),
));
}
unsafe { Ok(std::mem::transmute::<&Robj, &ExternalPtr<T>>(value)) }
}
}
impl<T: 'static> TryFrom<&mut Robj> for &mut ExternalPtr<T> {
type Error = Error;
fn try_from(value: &mut Robj) -> Result<Self> {
if !value.is_external_pointer() {
return Err(Error::ExpectedExternalPtr(value.clone()));
}
let boxed_ptr = unsafe {
value
.external_ptr_addr::<Box<dyn Any>>()
.cast_const()
.as_ref()
.ok_or_else(|| Error::ExpectedExternalNonNullPtr(value.clone()))?
};
if boxed_ptr.downcast_ref::<T>().is_none() {
return Err(Error::ExpectedExternalPtrType(
value.clone(),
std::any::type_name::<T>().to_string(),
));
}
unsafe { Ok(std::mem::transmute::<&mut Robj, &mut ExternalPtr<T>>(value)) }
}
}
impl<T: 'static> TryFrom<&Robj> for ExternalPtr<T> {
type Error = Error;
fn try_from(robj: &Robj) -> Result<Self> {
let result: &Self = robj.try_into()?;
Ok(result.clone())
}
}
impl<T: 'static> TryFrom<Robj> for ExternalPtr<T> {
type Error = Error;
fn try_from(robj: Robj) -> Result<Self> {
<ExternalPtr<T>>::try_from(&robj)
}
}
impl<T> From<ExternalPtr<T>> for Robj {
fn from(val: ExternalPtr<T>) -> Self {
val.robj
}
}
impl<T> From<Option<ExternalPtr<T>>> for Robj {
fn from(value: Option<ExternalPtr<T>>) -> Self {
match value {
None => nil_value(),
Some(value) => value.into(),
}
}
}
impl<T: Debug + 'static> std::fmt::Debug for ExternalPtr<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
(&**self as &T).fmt(f)
}
}
impl<T: 'static> AsRef<T> for ExternalPtr<T> {
fn as_ref(&self) -> &T {
self.addr()
}
}
impl<T: 'static> AsMut<T> for ExternalPtr<T> {
fn as_mut(&mut self) -> &mut T {
self.addr_mut()
}
}
#[cfg(test)]
mod tests {
use std::error::Error;
use super::*;
use extendr_engine::with_r;
#[derive(Debug)]
struct BareWrapper(i32);
#[test]
fn externalptr_is_ptr() -> std::result::Result<(), Box<dyn Error>> {
with_r(|| {
let a = BareWrapper(42);
let b = BareWrapper(42);
assert_eq!(a.0, b.0);
let a_ptr = std::ptr::addr_of!(a);
let b_ptr = std::ptr::addr_of!(b);
let a_externalptr = ExternalPtr::new(a);
let b_externalptr = ExternalPtr::new(b);
assert_ne!(
a_ptr, b_ptr,
"pointers has to be equal by address, not value"
);
assert_ne!(
a_externalptr.robj, b_externalptr.robj,
"R only knows about the pointer, and not the pointee"
);
assert_ne!(
a_externalptr, b_externalptr,
"ExternalPtr acts exactly like a pointer"
);
assert_ne!(&a_externalptr, &b_externalptr,);
Ok(())
})
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
struct Wrapper(i32);
#[test]
fn compare_externalptr_pointee() -> std::result::Result<(), Box<dyn Error>> {
with_r(|| {
let a = Wrapper(42);
let b = Wrapper(42);
let a_externalptr = ExternalPtr::new(a);
let b_externalptr = ExternalPtr::new(b);
assert_eq!(a_externalptr.as_ref(), b_externalptr.as_ref());
let a_externalptr = ExternalPtr::new(Wrapper(50));
let b_externalptr = ExternalPtr::new(Wrapper(60));
assert!(a_externalptr.as_ref() <= b_externalptr.as_ref());
assert_eq!(
a_externalptr.as_ref().max(b_externalptr.as_ref()),
&Wrapper(60)
);
Ok(())
})
}
}