use core::{marker::PhantomData, ops::Deref, pin::Pin};
use std::ops::DerefMut;
#[cfg(nightly)]
use std::{marker::Unsize, ops::DispatchFromDyn, ops::Receiver};
use cxx::{memory::UniquePtrTarget, UniquePtr};
#[repr(transparent)]
pub struct CppRef<T: ?Sized>(*const T);
impl<T: ?Sized> CppRef<T> {
pub fn as_ptr(&self) -> *const T {
self.0
}
pub unsafe fn as_ref(&self) -> &T {
&*self.as_ptr()
}
pub fn from_ptr(ptr: *const T) -> Self {
Self(ptr)
}
pub fn const_cast(&self) -> CppMutRef<T> {
CppMutRef(self.0 as *mut T)
}
}
#[cfg(nightly)]
impl<T: ?Sized> Receiver for CppRef<T> {
type Target = T;
}
impl<T: ?Sized> Clone for CppRef<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized> Copy for CppRef<T> {}
#[cfg(nightly)]
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<CppRef<U>> for CppRef<T> {}
#[repr(transparent)]
pub struct CppLtRef<'a, T: ?Sized> {
ptr: CppRef<T>,
phantom: PhantomData<&'a T>,
}
impl<T: ?Sized> Deref for CppLtRef<'_, T> {
type Target = CppRef<T>;
fn deref(&self) -> &Self::Target {
unsafe { std::mem::transmute(self) }
}
}
impl<T: ?Sized> Clone for CppLtRef<'_, T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized> Copy for CppLtRef<'_, T> {}
impl<T: ?Sized> CppLtRef<'_, T> {
pub fn lifetime_cast(&self) -> CppRef<T> {
CppRef(self.ptr.as_ptr())
}
pub fn from_ptr(ptr: *const T) -> Self {
Self {
ptr: CppRef::from_ptr(ptr),
phantom: PhantomData,
}
}
}
#[repr(transparent)]
pub struct CppMutRef<T: ?Sized>(*mut T);
impl<T: ?Sized> CppMutRef<T> {
pub fn as_mut_ptr(&self) -> *mut T {
self.0
}
pub unsafe fn as_mut(&mut self) -> &mut T {
&mut *self.as_mut_ptr()
}
pub fn from_ptr(ptr: *mut T) -> Self {
Self(ptr)
}
}
impl<T: ?Sized> Deref for CppMutRef<T> {
type Target = CppRef<T>;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { std::mem::transmute(self) }
}
}
impl<T: ?Sized> Clone for CppMutRef<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized> Copy for CppMutRef<T> {}
impl<T> From<CppMutRef<T>> for CppRef<T> {
fn from(mutable: CppMutRef<T>) -> Self {
Self(mutable.0)
}
}
#[repr(transparent)]
pub struct CppMutLtRef<'a, T: ?Sized> {
ptr: CppMutRef<T>,
phantom: PhantomData<&'a mut T>,
}
impl<T: ?Sized> CppMutLtRef<'_, T> {
pub fn lifetime_cast(&mut self) -> CppMutRef<T> {
CppMutRef(self.ptr.as_mut_ptr())
}
pub fn from_ptr(ptr: *mut T) -> Self {
Self {
ptr: CppMutRef::from_ptr(ptr),
phantom: PhantomData,
}
}
}
#[cfg(nightly)]
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<CppMutRef<U>> for CppMutRef<T> {}
pub trait AsCppRef<T: ?Sized> {
fn as_cpp_ref(&self) -> CppRef<T>;
}
pub trait AsCppMutRef<T: ?Sized>: AsCppRef<T> {
fn as_cpp_mut_ref(&mut self) -> CppMutRef<T>;
}
impl<T: ?Sized> AsCppRef<T> for CppMutRef<T> {
fn as_cpp_ref(&self) -> CppRef<T> {
CppRef::from_ptr(self.0 as *const T)
}
}
#[repr(transparent)]
struct CppPinContents<T: ?Sized>(T);
impl<T: ?Sized> CppPinContents<T> {
fn addr_of(&self) -> *const T {
std::ptr::addr_of!(self.0)
}
fn addr_of_mut(&mut self) -> *mut T {
std::ptr::addr_of_mut!(self.0)
}
}
pub struct CppPin<T: ?Sized>(Box<CppPinContents<T>>, CppMutRef<T>);
impl<T: ?Sized> CppPin<T> {
pub fn new(item: T) -> Self
where
T: Sized,
{
let mut contents = Box::new(CppPinContents(item));
let ptr = contents.addr_of_mut();
Self(contents, CppMutRef(ptr))
}
pub fn from_box(item: Box<T>) -> Self {
let mut contents = unsafe { std::mem::transmute::<Box<T>, Box<CppPinContents<T>>>(item) };
let ptr = contents.addr_of_mut();
Self(contents, CppMutRef(ptr))
}
pub fn from_pinned_box(item: Pin<Box<T>>) -> Self {
Self::from_box(unsafe { Pin::into_inner_unchecked(item) })
}
pub fn as_ptr(&self) -> *const T {
self.0.addr_of()
}
pub fn as_mut_ptr(&mut self) -> *mut T {
self.0.addr_of_mut()
}
pub unsafe fn as_ref(&self) -> &T {
&*self.as_ptr()
}
pub unsafe fn as_mut(&mut self) -> &mut T {
&mut *self.as_mut_ptr()
}
pub unsafe fn extract(self) -> Box<T> {
std::mem::transmute(self.0)
}
}
impl<T: ?Sized> AsCppRef<T> for CppPin<T> {
fn as_cpp_ref(&self) -> CppRef<T> {
CppRef::from_ptr(self.as_ptr())
}
}
impl<T: ?Sized> AsCppMutRef<T> for CppPin<T> {
fn as_cpp_mut_ref(&mut self) -> CppMutRef<T> {
CppMutRef::from_ptr(self.as_mut_ptr())
}
}
impl<T: ?Sized> Deref for CppPin<T> {
type Target = CppMutRef<T>;
fn deref(&self) -> &Self::Target {
&self.1
}
}
impl<T: ?Sized> DerefMut for CppPin<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.1
}
}
pub struct CppUniquePtrPin<T: UniquePtrTarget>(UniquePtr<T>, CppMutRef<T>);
impl<T: UniquePtrTarget> CppUniquePtrPin<T> {
pub fn new(item: UniquePtr<T>) -> Self {
let ptr = item.as_mut_ptr();
Self(item, CppMutRef::from_ptr(ptr))
}
pub fn as_ptr(&self) -> *const T {
self.0
.as_ref()
.expect("UniquePtr was null; we can't make a C++ reference")
}
}
impl<T: UniquePtrTarget> AsCppRef<T> for CppUniquePtrPin<T> {
fn as_cpp_ref(&self) -> CppRef<T> {
CppRef::from_ptr(self.as_ptr())
}
}
impl<T: UniquePtrTarget> AsCppMutRef<T> for CppUniquePtrPin<T> {
fn as_cpp_mut_ref(&mut self) -> CppMutRef<T> {
self.1
}
}
impl<T: UniquePtrTarget> Deref for CppUniquePtrPin<T> {
type Target = CppMutRef<T>;
fn deref(&self) -> &Self::Target {
&self.1
}
}
impl<T: UniquePtrTarget> AsCppRef<T> for cxx::UniquePtr<T> {
fn as_cpp_ref(&self) -> CppRef<T> {
CppRef::from_ptr(self.as_ptr())
}
}
#[cfg(all(feature = "arbitrary_self_types", test))]
mod tests {
use super::*;
struct CppOuter {
_a: u32,
inner: CppInner,
global: *const CppInner,
}
impl CppOuter {
fn get_inner_ref<'a>(self: &CppRef<'a, CppOuter>) -> CppRef<'a, CppInner> {
let self_rust_ref = unsafe { self.as_ref() };
CppRef::from_ptr(std::ptr::addr_of!(self_rust_ref.inner))
}
fn get_global_ref<'a>(self: &CppRef<'a, CppOuter>) -> CppRef<'a, CppInner> {
let self_rust_ref = unsafe { self.as_ref() };
CppRef::from_ptr(self_rust_ref.global)
}
}
struct CppInner {
b: u32,
}
impl CppInner {
fn value_is(self: &CppRef<Self>) -> u32 {
let self_rust_ref = unsafe { self.as_ref() };
self_rust_ref.b
}
}
#[test]
fn cpp_objects() {
let mut global = CppInner { b: 7 };
let global_ref_lifetime_phantom;
{
let outer = CppOuter {
_a: 12,
inner: CppInner { b: 3 },
global: &mut global,
};
let outer = CppPin::new(outer);
let inner_ref = outer.as_cpp_ref().get_inner_ref();
assert_eq!(inner_ref.value_is(), 3);
global_ref_lifetime_phantom = Some(outer.as_cpp_ref().get_global_ref().lifetime_cast());
}
let global_ref = global_ref_lifetime_phantom.unwrap();
let global_ref = global_ref.as_cpp_ref();
assert_eq!(global_ref.value_is(), 7);
}
#[test]
fn cpp_pin() {
let a = RustThing { _a: 4 };
let a = CppPin::new(a);
let _ = a.as_cpp_ref();
let _ = a.as_cpp_ref();
}
}