Repository: dtolnay/cxx
Pull request #988 (2021-12-26 20:21:12)

Add Vec<T>::truncate to C++ API
Based on the model of #951, which added `Vec<T>::clear`. The `truncate`
method is the more general form of clear.

(If we wanted to, we could delete the binding for `clear` and implement
`clear` as a call to `truncate(0)` on the C++ side, but seemed worth
leaving in in case the Rust `clear` implementation one day gets
smarter.)


Patch:
diff --git a/book/src/binding/vec.md b/book/src/binding/vec.md
index a02b93c7..4d6587ab 100644
--- a/book/src/binding/vec.md
+++ b/book/src/binding/vec.md
@@ -47,6 +47,7 @@ public:
   void push_back(T &&value);
   template <typename... Args>
   void emplace_back(Args &&...args);
+  void truncate(size_t len);
   void clear();
 
   class iterator;
diff --git a/gen/src/write.rs b/gen/src/write.rs
index 64d83110..43e2d2fa 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -1475,6 +1475,11 @@ fn write_rust_vec_extern(out: &mut OutFile, key: NamedImplKey) {
         "void cxxbridge1$rust_vec${}$set_len(::rust::Vec<{}> *ptr, ::std::size_t len) noexcept;",
         instance, inner,
     );
+    writeln!(
+        out,
+        "void cxxbridge1$rust_vec${}$truncate(::rust::Vec<{}> *ptr, ::std::size_t len) noexcept;",
+        instance, inner,
+    );
     writeln!(
         out,
         "void cxxbridge1$rust_vec${}$clear(::rust::Vec<{}> *ptr) noexcept;",
@@ -1591,6 +1596,16 @@ fn write_rust_vec_impl(out: &mut OutFile, key: NamedImplKey) {
     );
     writeln!(out, "}}");
 
+    writeln!(out, "template <>");
+    begin_function_definition(out);
+    writeln!(out, "void Vec<{}>::truncate(::std::size_t len) {{", inner,);
+    writeln!(
+        out,
+        "  return cxxbridge1$rust_vec${}$truncate(this, len);",
+        instance,
+    );
+    writeln!(out, "}}");
+
     writeln!(out, "template <>");
     begin_function_definition(out);
     writeln!(out, "void Vec<{}>::clear() {{", inner);
diff --git a/include/cxx.h b/include/cxx.h
index 36839515..857247cb 100644
--- a/include/cxx.h
+++ b/include/cxx.h
@@ -324,6 +324,7 @@ public:
   void push_back(T &&value);
   template <typename... Args>
   void emplace_back(Args &&...args);
+  void truncate(std::size_t len);
   void clear();
 
   using iterator = typename Slice<T>::iterator;
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index 1e3c8da6..8c1389bc 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -1288,6 +1288,7 @@ fn expand_rust_vec(key: NamedImplKey, types: &Types, explicit_impl: Option<&Impl
     let link_data = format!("{}data", link_prefix);
     let link_reserve_total = format!("{}reserve_total", link_prefix);
     let link_set_len = format!("{}set_len", link_prefix);
+    let link_truncate = format!("{}truncate", link_prefix);
     let link_clear = format!("{}clear", link_prefix);
 
     let local_prefix = format_ident!("{}__vec_", elem);
@@ -1298,6 +1299,7 @@ fn expand_rust_vec(key: NamedImplKey, types: &Types, explicit_impl: Option<&Impl
     let local_data = format_ident!("{}data", local_prefix);
     let local_reserve_total = format_ident!("{}reserve_total", local_prefix);
     let local_set_len = format_ident!("{}set_len", local_prefix);
+    let local_truncate = format_ident!("{}truncate", local_prefix);
     let local_clear = format_ident!("{}clear", local_prefix);
 
     let (impl_generics, ty_generics) = generics::split_for_impl(key, explicit_impl, resolve);
@@ -1353,6 +1355,12 @@ fn expand_rust_vec(key: NamedImplKey, types: &Types, explicit_impl: Option<&Impl
             (*this).set_len(len);
         }
         #[doc(hidden)]
+        #[export_name = #link_truncate]
+        unsafe extern "C" fn #local_truncate #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>, len: usize) {
+            let __fn = concat!("<", module_path!(), #prevent_unwind_drop_label);
+            ::cxx::private::prevent_unwind(__fn, || (*this).truncate(len));
+        }
+        #[doc(hidden)]
         #[export_name = #link_clear]
         unsafe extern "C" fn #local_clear #impl_generics(this: *mut ::cxx::private::RustVec<#elem #ty_generics>) {
             let __fn = concat!("<", module_path!(), #prevent_unwind_drop_label);
diff --git a/src/cxx.cc b/src/cxx.cc
index 4bdbd583..7fffa834 100644
--- a/src/cxx.cc
+++ b/src/cxx.cc
@@ -569,6 +569,8 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *),
       rust::Vec<CXX_TYPE> *ptr, std::size_t new_cap) noexcept;                 \
   void cxxbridge1$rust_vec$##RUST_TYPE##$set_len(rust::Vec<CXX_TYPE> *ptr,     \
                                                  std::size_t len) noexcept;    \
+  void cxxbridge1$rust_vec$##RUST_TYPE##$truncate(rust::Vec<CXX_TYPE> *ptr,    \
+                                                  std::size_t len) noexcept;   \
   void cxxbridge1$rust_vec$##RUST_TYPE##$clear(                                \
       rust::Vec<CXX_TYPE> *ptr) noexcept;
 
@@ -602,6 +604,10 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *),
     cxxbridge1$rust_vec$##RUST_TYPE##$set_len(this, len);                      \
   }                                                                            \
   template <>                                                                  \
+  void Vec<CXX_TYPE>::truncate(std::size_t len) {                              \
+    cxxbridge1$rust_vec$##RUST_TYPE##$truncate(this, len);                     \
+  }                                                                            \
+  template <>                                                                  \
   void Vec<CXX_TYPE>::clear() {                                                \
     cxxbridge1$rust_vec$##RUST_TYPE##$clear(this);                             \
   }
diff --git a/src/rust_vec.rs b/src/rust_vec.rs
index 47d49a7b..d471be6d 100644
--- a/src/rust_vec.rs
+++ b/src/rust_vec.rs
@@ -69,6 +69,10 @@ impl<T> RustVec<T> {
         unsafe { self.as_mut_vec().set_len(len) }
     }
 
+    pub fn truncate(&mut self, len: usize) {
+        self.as_mut_vec().truncate(len)
+    }
+
     pub fn clear(&mut self) {
         self.as_mut_vec().clear();
     }
diff --git a/src/symbols/rust_vec.rs b/src/symbols/rust_vec.rs
index 4613db3a..221e087c 100644
--- a/src/symbols/rust_vec.rs
+++ b/src/symbols/rust_vec.rs
@@ -56,6 +56,12 @@ macro_rules! rust_vec_shims {
                     unsafe { (*this).set_len(len) }
                 }
             }
+            attr! {
+                #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$truncate")]
+                unsafe extern "C" fn __truncate(this: *mut RustVec<$ty>, len: usize) {
+                    unsafe { (*this).truncate(len) }
+                }
+            }
             attr! {
                 #[export_name = concat!("cxxbridge1$rust_vec$", $segment, "$clear")]
                 unsafe extern "C" fn __clear(this: *mut RustVec<$ty>) {


Test patch:
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index 8981178b..98689702 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -149,6 +149,7 @@ pub mod ffi {
         fn c_take_rust_vec_index(v: Vec<u8>);
         fn c_take_rust_vec_shared_index(v: Vec<Shared>);
         fn c_take_rust_vec_shared_push(v: Vec<Shared>);
+        fn c_take_rust_vec_shared_truncate(v: Vec<Shared>);
         fn c_take_rust_vec_shared_clear(v: Vec<Shared>);
         fn c_take_rust_vec_shared_forward_iterator(v: Vec<Shared>);
         fn c_take_rust_vec_shared_sort(v: Vec<Shared>);
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index f85d2ec2..ac7738df 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -452,6 +452,16 @@ void c_take_rust_vec_shared_push(rust::Vec<Shared> v) {
   }
 }
 
+void c_take_rust_vec_shared_truncate(rust::Vec<Shared> v) {
+  v.truncate(1);
+  if (v.size() == 1) {
+    v.truncate(0);
+    if (v.size() == 0) {
+      cxx_test_suite_set_correct();
+    }
+  }
+}
+
 void c_take_rust_vec_shared_clear(rust::Vec<Shared> v) {
   v.clear();
   if (v.size() == 0) {
diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h
index 52d853a0..13e9c0f7 100644
--- a/tests/ffi/tests.h
+++ b/tests/ffi/tests.h
@@ -152,6 +152,7 @@ void c_take_rust_vec_nested_ns_shared(rust::Vec<::A::B::ABShared> v);
 void c_take_rust_vec_string(rust::Vec<rust::String> v);
 void c_take_rust_vec_shared_index(rust::Vec<Shared> v);
 void c_take_rust_vec_shared_push(rust::Vec<Shared> v);
+void c_take_rust_vec_shared_truncate(rust::Vec<Shared> v);
 void c_take_rust_vec_shared_clear(rust::Vec<Shared> v);
 void c_take_rust_vec_shared_forward_iterator(rust::Vec<Shared> v);
 void c_take_rust_vec_shared_sort(rust::Vec<Shared> v);
diff --git a/tests/test.rs b/tests/test.rs
index d536a359..8279b752 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -167,6 +167,9 @@ fn test_c_take() {
     check!(ffi::c_take_rust_vec_shared(shared_test_vec.clone()));
     check!(ffi::c_take_rust_vec_shared_index(shared_test_vec.clone()));
     check!(ffi::c_take_rust_vec_shared_push(shared_test_vec.clone()));
+    check!(ffi::c_take_rust_vec_shared_truncate(
+        shared_test_vec.clone()
+    ));
     check!(ffi::c_take_rust_vec_shared_clear(shared_test_vec.clone()));
     check!(ffi::c_take_rust_vec_shared_forward_iterator(
         shared_test_vec,