Repository: dtolnay/cxx (language: rust)

Issue:
How to pass a C++ cxx:SharedPtr<T> pointer to primitive type (u8, u16) to rust?
Hi all,

I want to pass a C++ shared pointer to rust, the pointed type is u32. But if I coded as:
```rust
pub fn create_image(data: SharedPtr<u32>) -> SharedPtr<Image> {
    unimplemented!()
}
```
the compiler will report "the trait `SharedPtrTarget` is not implemented for `u32`"。

How can fix that? Thanks. 


Discussion / hints:

Fix patch:
diff --git a/.clang-tidy b/.clang-tidy
index 1d4571362..b94f5a9d9 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -14,5 +14,6 @@ Checks:
   -cppcoreguidelines-pro-type-reinterpret-cast,
   -cppcoreguidelines-special-member-functions,
   -modernize-use-default-member-init,
+  -modernize-use-equals-default,
   -modernize-use-trailing-return-type,
 HeaderFilterRegex: cxx\.h
diff --git a/src/cxx.cc b/src/cxx.cc
index ac10df181..2be483d56 100644
--- a/src/cxx.cc
+++ b/src/cxx.cc
@@ -331,6 +331,15 @@ Error &Error::operator=(Error &&other) noexcept {
 
 const char *Error::what() const noexcept { return this->msg; }
 
+namespace {
+template <typename T>
+union MaybeUninit {
+  T value;
+  MaybeUninit() {}
+  ~MaybeUninit() {}
+};
+} // namespace
+
 } // namespace cxxbridge1
 } // namespace rust
 
@@ -443,6 +452,34 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *),
     return cxxbridge1$rust_vec$##RUST_TYPE##$stride();                         \
   }
 
+#define SHARED_PTR_OPS(RUST_TYPE, CXX_TYPE)                                    \
+  static_assert(sizeof(std::shared_ptr<CXX_TYPE>) == 2 * sizeof(void *), "");  \
+  static_assert(alignof(std::shared_ptr<CXX_TYPE>) == alignof(void *), "");    \
+  void cxxbridge1$std$shared_ptr$##RUST_TYPE##$null(                           \
+      std::shared_ptr<CXX_TYPE> *ptr) noexcept {                               \
+    new (ptr) std::shared_ptr<CXX_TYPE>();                                     \
+  }                                                                            \
+  CXX_TYPE *cxxbridge1$std$shared_ptr$##RUST_TYPE##$uninit(                    \
+      std::shared_ptr<CXX_TYPE> *ptr) noexcept {                               \
+    CXX_TYPE *uninit =                                                         \
+        reinterpret_cast<CXX_TYPE *>(new rust::MaybeUninit<CXX_TYPE>);         \
+    new (ptr) std::shared_ptr<CXX_TYPE>(uninit);                               \
+    return uninit;                                                             \
+  }                                                                            \
+  void cxxbridge1$std$shared_ptr$##RUST_TYPE##$clone(                          \
+      const std::shared_ptr<CXX_TYPE> &self,                                   \
+      std::shared_ptr<CXX_TYPE> *ptr) noexcept {                               \
+    new (ptr) std::shared_ptr<CXX_TYPE>(self);                                 \
+  }                                                                            \
+  const CXX_TYPE *cxxbridge1$std$shared_ptr$##RUST_TYPE##$get(                 \
+      const std::shared_ptr<CXX_TYPE> &self) noexcept {                        \
+    return self.get();                                                         \
+  }                                                                            \
+  void cxxbridge1$std$shared_ptr$##RUST_TYPE##$drop(                           \
+      const std::shared_ptr<CXX_TYPE> *self) noexcept {                        \
+    self->~shared_ptr();                                                       \
+  }
+
 // Usize and isize are the same type as one of the below.
 #define FOR_EACH_NUMERIC(MACRO)                                                \
   MACRO(u8, uint8_t)                                                           \
@@ -468,9 +505,16 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *),
   MACRO(char, char)                                                            \
   MACRO(string, rust::String)
 
+#define FOR_EACH_SHARED_PTR(MACRO)                                             \
+  FOR_EACH_NUMERIC(MACRO)                                                      \
+  MACRO(usize, size_t)                                                         \
+  MACRO(isize, rust::isize)                                                    \
+  MACRO(string, std::string)
+
 extern "C" {
 FOR_EACH_STD_VECTOR(STD_VECTOR_OPS)
 FOR_EACH_RUST_VEC(RUST_VEC_EXTERNS)
+FOR_EACH_SHARED_PTR(SHARED_PTR_OPS)
 } // extern "C"
 
 namespace rust {
diff --git a/src/shared_ptr.rs b/src/shared_ptr.rs
index 7fdb3566b..63ad7d31b 100644
--- a/src/shared_ptr.rs
+++ b/src/shared_ptr.rs
@@ -1,3 +1,4 @@
+use crate::cxx_string::CxxString;
 use crate::kind::Trivial;
 use crate::ExternType;
 use core::ffi::c_void;
@@ -189,3 +190,77 @@ pub unsafe trait SharedPtrTarget {
     #[doc(hidden)]
     unsafe fn __drop(this: *mut c_void);
 }
+
+macro_rules! impl_shared_ptr_target {
+    ($segment:expr, $name:expr, $ty:ty) => {
+        unsafe impl SharedPtrTarget for $ty {
+            const __NAME: &'static dyn Display = &$name;
+            unsafe fn __null(new: *mut c_void) {
+                extern "C" {
+                    attr! {
+                        #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$null")]
+                        fn __null(new: *mut c_void);
+                    }
+                }
+                __null(new);
+            }
+            unsafe fn __new(value: Self, new: *mut c_void) {
+                extern "C" {
+                    attr! {
+                        #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$uninit")]
+                        fn __uninit(new: *mut c_void) -> *mut c_void;
+                    }
+                }
+                __uninit(new).cast::<$ty>().write(value);
+            }
+            unsafe fn __clone(this: *const c_void, new: *mut c_void) {
+                extern "C" {
+                    attr! {
+                        #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$clone")]
+                        fn __clone(this: *const c_void, new: *mut c_void);
+                    }
+                }
+                __clone(this, new);
+            }
+            unsafe fn __get(this: *const c_void) -> *const Self {
+                extern "C" {
+                    attr! {
+                        #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$get")]
+                        fn __get(this: *const c_void) -> *const c_void;
+                    }
+                }
+                __get(this).cast()
+            }
+            unsafe fn __drop(this: *mut c_void) {
+                extern "C" {
+                    attr! {
+                        #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$drop")]
+                        fn __drop(this: *mut c_void);
+                    }
+                }
+                __drop(this);
+            }
+        }
+    };
+}
+
+macro_rules! impl_shared_ptr_target_for_primitive {
+    ($ty:ident) => {
+        impl_shared_ptr_target!(stringify!($ty), stringify!($ty), $ty);
+    };
+}
+
+impl_shared_ptr_target_for_primitive!(u8);
+impl_shared_ptr_target_for_primitive!(u16);
+impl_shared_ptr_target_for_primitive!(u32);
+impl_shared_ptr_target_for_primitive!(u64);
+impl_shared_ptr_target_for_primitive!(usize);
+impl_shared_ptr_target_for_primitive!(i8);
+impl_shared_ptr_target_for_primitive!(i16);
+impl_shared_ptr_target_for_primitive!(i32);
+impl_shared_ptr_target_for_primitive!(i64);
+impl_shared_ptr_target_for_primitive!(isize);
+impl_shared_ptr_target_for_primitive!(f32);
+impl_shared_ptr_target_for_primitive!(f64);
+
+impl_shared_ptr_target!("string", "CxxString", CxxString);


Test patch: