Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UniquePtr::to_shared and SharedPtr::from_unmanaged #1005

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
12 changes: 12 additions & 0 deletions gen/src/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,18 @@ fn write_shared_ptr(out: &mut OutFile, key: NamedImplKey) {
writeln!(out, "}}");
}
begin_function_definition(out);
writeln!(
out,
"void cxxbridge1$shared_ptr${}$from_unmanaged(::std::shared_ptr<{}>* ptr, void* data) noexcept {{",
instance, inner,
);
writeln!(
out,
"new (ptr) std::shared_ptr<{}>(static_cast<{}*>(data));",
inner, inner
);
writeln!(out, "}}");
begin_function_definition(out);
writeln!(
out,
"void cxxbridge1$shared_ptr${}$clone(::std::shared_ptr<{}> const &self, ::std::shared_ptr<{}> *ptr) noexcept {{",
Expand Down
8 changes: 8 additions & 0 deletions macro/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1479,6 +1479,7 @@ fn expand_shared_ptr(
let prefix = format!("cxxbridge1$shared_ptr${}$", resolve.name.to_symbol());
let link_null = format!("{}null", prefix);
let link_uninit = format!("{}uninit", prefix);
let link_from_unmanaged = format!("{}from_unmanaged", prefix);
let link_clone = format!("{}clone", prefix);
let link_get = format!("{}get", prefix);
let link_drop = format!("{}drop", prefix);
Expand Down Expand Up @@ -1517,6 +1518,13 @@ fn expand_shared_ptr(
__null(new);
}
#new_method
unsafe fn __from_unmanaged(value: *mut Self, new: *mut ::cxx::core::ffi::c_void) {
extern "C" {
#[link_name = #link_from_unmanaged]
fn __from_unmanaged(new: *const ::cxx::core::ffi::c_void, value: *mut ::cxx::core::ffi::c_void);
}
__from_unmanaged(new, value as *mut ::cxx::core::ffi::c_void);
}
unsafe fn __clone(this: *const ::cxx::core::ffi::c_void, new: *mut ::cxx::core::ffi::c_void) {
extern "C" {
#[link_name = #link_clone]
Expand Down
4 changes: 4 additions & 0 deletions src/cxx.cc
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,10 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *),
std::shared_ptr<CXX_TYPE> *ptr) noexcept { \
new (ptr) std::shared_ptr<CXX_TYPE>(); \
} \
void cxxbridge1$std$shared_ptr$##RUST_TYPE##$from_unmanaged( \
std::shared_ptr<CXX_TYPE> *ptr, void* data) noexcept { \
new (ptr) std::shared_ptr<CXX_TYPE>(static_cast<CXX_TYPE*>(data)); \
} \
CXX_TYPE *cxxbridge1$std$shared_ptr$##RUST_TYPE##$uninit( \
std::shared_ptr<CXX_TYPE> *ptr) noexcept { \
CXX_TYPE *uninit = \
Expand Down
31 changes: 31 additions & 0 deletions src/shared_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,26 @@ where
}
}

/// Create a shared pointer from an already-allocated object
/// Corresponds to constructor (3) of [`std::shared_ptr`](https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr)
///
/// The SharedPtr gains ownership of the pointer and will call `std::default_delete` on it when the refcount goes to zero.
/// The data will not be moved, so any pointers to this data elsewhere in the program continue to be valid
///
/// # Safety
///
/// * Value must either be null or point to a valid instance of T
/// * Value must not be deleted (as the `std::shared_ptr` now manages its lifetime)
/// * Value must not be accessed after the last `std::shared_ptr` is dropped
pub unsafe fn from_unmanaged(value: *mut T) -> Self {
let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
let new = shared_ptr.as_mut_ptr().cast();
unsafe {
T::__from_unmanaged(value, new);
shared_ptr.assume_init()
}
}

/// Checks whether the SharedPtr does not own an object.
///
/// This is the opposite of [std::shared_ptr\<T\>::operator bool](https://en.cppreference.com/w/cpp/memory/shared_ptr/operator_bool).
Expand Down Expand Up @@ -198,6 +218,8 @@ pub unsafe trait SharedPtrTarget {
unreachable!()
}
#[doc(hidden)]
unsafe fn __from_unmanaged(value: *mut Self, new: *mut c_void);
#[doc(hidden)]
unsafe fn __clone(this: *const c_void, new: *mut c_void);
#[doc(hidden)]
unsafe fn __get(this: *const c_void) -> *const Self;
Expand Down Expand Up @@ -229,6 +251,15 @@ macro_rules! impl_shared_ptr_target {
}
unsafe { __uninit(new).cast::<$ty>().write(value) }
}
unsafe fn __from_unmanaged(value: *mut Self, new: *mut c_void) {
extern "C" {
attr! {
#[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$from_unmanaged")]
fn __from_unmanaged(new: *mut c_void, value: *mut c_void);
}
}
unsafe { __from_unmanaged(new, value as *mut c_void) }
}
unsafe fn __clone(this: *const c_void, new: *mut c_void) {
extern "C" {
attr! {
Expand Down
13 changes: 12 additions & 1 deletion src/unique_ptr.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::cxx_vector::{CxxVector, VectorElement};
use crate::fmt::display;
use crate::kind::Trivial;
use crate::memory::SharedPtrTarget;
use crate::string::CxxString;
use crate::ExternType;
use crate::{ExternType, SharedPtr};
use core::ffi::c_void;
use core::fmt::{self, Debug, Display};
use core::marker::PhantomData;
Expand Down Expand Up @@ -109,6 +110,16 @@ where
}
}

impl<T> UniquePtr<T>
where
T: UniquePtrTarget + SharedPtrTarget,
{
/// Convert this UniquePtr to a SharedPtr, analogous to constructor (13) for [`std::shared_ptr`](https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr)
pub fn to_shared(self) -> SharedPtr<T> {
unsafe { SharedPtr::from_unmanaged(self.into_raw()) }
}
}

unsafe impl<T> Send for UniquePtr<T> where T: Send + UniquePtrTarget {}
unsafe impl<T> Sync for UniquePtr<T> where T: Sync + UniquePtrTarget {}

Expand Down
25 changes: 25 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,31 @@ fn test_shared_ptr_weak_ptr() {
assert!(weak_ptr.upgrade().is_null());
}

#[test]
fn test_unique_to_shared_ptr_string() {
let unique = ffi::c_return_unique_ptr_string();
let ptr = &*unique as *const _;
let shared = unique.to_shared();
assert_eq!(&*shared as *const _, ptr);
assert_eq!(&*shared, "2020");
}

#[test]
fn test_unique_to_shared_ptr_cpp_type() {
let unique = ffi::c_return_unique_ptr();
let ptr = &*unique as *const _;
let shared = unique.to_shared();
assert_eq!(&*shared as *const _, ptr);
}

#[test]
fn test_unique_to_shared_ptr_null() {
let unique = cxx::UniquePtr::<ffi::C>::null();
assert!(unique.is_null());
let shared = unique.to_shared();
assert!(shared.is_null());
}

#[test]
fn test_c_ns_method_calls() {
let unique_ptr = ffi2::ns_c_return_unique_ptr_ns();
Expand Down