Repository: dtolnay/cxx
Pull request #597 (2020-12-27 20:25:22)

Add rust::size_of, align_of accessors that work for opaque types
```diff
  namespace rust {
+ template <typename T> size_t size_of();
+ template <typename T> size_t align_of();
  }
```

Using `rust::size_of<T>()` / `rust::align_of<T>()` produces an accurate size/align for opaque Rust types (via the symbols from #595) as well as shared data structures and native C++ types.

Support for opaque Rust types in slices (#532) can be easily implemented on top of these.


Patch:
diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs
index dbd019b2..8ce89f80 100644
--- a/gen/src/builtin.rs
+++ b/gen/src/builtin.rs
@@ -13,6 +13,7 @@ pub struct Builtins<'a> {
     pub rust_fn: bool,
     pub rust_isize: bool,
     pub opaque: bool,
+    pub layout: bool,
     pub unsafe_bitcopy: bool,
     pub rust_error: bool,
     pub manually_drop: bool,
@@ -102,6 +103,12 @@ pub(super) fn write(out: &mut OutFile) {
         include.type_traits = true;
     }
 
+    if builtin.layout {
+        include.type_traits = true;
+        include.cstddef = true;
+        builtin.is_complete = true;
+    }
+
     if builtin.is_complete {
         include.cstddef = true;
         include.type_traits = true;
@@ -125,10 +132,13 @@ pub(super) fn write(out: &mut OutFile) {
         out.end_block(Block::AnonymousNamespace);
     }
 
+    out.next_section();
     if builtin.rust_str && !builtin.rust_string {
-        out.next_section();
         writeln!(out, "class String;");
     }
+    if builtin.layout && !builtin.opaque {
+        writeln!(out, "class Opaque;");
+    }
 
     ifndef::write(out, builtin.rust_string, "CXXBRIDGE1_RUST_STRING");
     ifndef::write(out, builtin.rust_str, "CXXBRIDGE1_RUST_STR");
@@ -140,8 +150,9 @@ pub(super) fn write(out: &mut OutFile) {
     ifndef::write(out, builtin.rust_error, "CXXBRIDGE1_RUST_ERROR");
     ifndef::write(out, builtin.rust_isize, "CXXBRIDGE1_RUST_ISIZE");
     ifndef::write(out, builtin.opaque, "CXXBRIDGE1_RUST_OPAQUE");
-    ifndef::write(out, builtin.relocatable, "CXXBRIDGE1_RELOCATABLE");
     ifndef::write(out, builtin.is_complete, "CXXBRIDGE1_IS_COMPLETE");
+    ifndef::write(out, builtin.layout, "CXXBRIDGE1_LAYOUT");
+    ifndef::write(out, builtin.relocatable, "CXXBRIDGE1_RELOCATABLE");
 
     out.begin_block(Block::Namespace("detail"));
 
diff --git a/gen/src/write.rs b/gen/src/write.rs
index e20f54f9..bae9cfee 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -333,8 +333,10 @@ fn write_opaque_type<'a>(out: &mut OutFile<'a>, ety: &'a ExternType, methods: &[
         writeln!(out);
     }
 
+    out.builtin.layout = true;
     out.include.cstddef = true;
     writeln!(out, "private:");
+    writeln!(out, "  friend ::rust::layout;");
     writeln!(out, "  struct layout {{");
     writeln!(out, "    static ::std::size_t size() noexcept;");
     writeln!(out, "    static ::std::size_t align() noexcept;");
diff --git a/include/cxx.h b/include/cxx.h
index 25a62527..566b4822 100644
--- a/include/cxx.h
+++ b/include/cxx.h
@@ -401,6 +401,11 @@ public:
 };
 #endif // CXXBRIDGE1_RUST_OPAQUE
 
+template <typename T>
+std::size_t size_of();
+template <typename T>
+std::size_t align_of();
+
 // IsRelocatable<T> is used in assertions that a C++ type passed by value
 // between Rust and C++ is soundly relocatable by Rust.
 //
@@ -987,6 +992,62 @@ struct is_complete<T, decltype(sizeof(T))> : std::true_type {};
 } // namespace detail
 #endif // CXXBRIDGE1_IS_COMPLETE
 
+#ifndef CXXBRIDGE1_LAYOUT
+#define CXXBRIDGE1_LAYOUT
+class layout {
+  template <typename T>
+  friend std::size_t size_of();
+  template <typename T>
+  friend std::size_t align_of();
+  template <typename T>
+  static typename std::enable_if<std::is_base_of<Opaque, T>::value,
+                                 std::size_t>::type
+  do_size_of() {
+    return T::layout::size();
+  }
+  template <typename T>
+  static typename std::enable_if<!std::is_base_of<Opaque, T>::value,
+                                 std::size_t>::type
+  do_size_of() {
+    return sizeof(T);
+  }
+  template <typename T>
+  static
+      typename std::enable_if<detail::is_complete<T>::value, std::size_t>::type
+      size_of() {
+    return do_size_of<T>();
+  }
+  template <typename T>
+  static typename std::enable_if<std::is_base_of<Opaque, T>::value,
+                                 std::size_t>::type
+  do_align_of() {
+    return T::layout::align();
+  }
+  template <typename T>
+  static typename std::enable_if<!std::is_base_of<Opaque, T>::value,
+                                 std::size_t>::type
+  do_align_of() {
+    return alignof(T);
+  }
+  template <typename T>
+  static
+      typename std::enable_if<detail::is_complete<T>::value, std::size_t>::type
+      align_of() {
+    return do_align_of<T>();
+  }
+};
+
+template <typename T>
+std::size_t size_of() {
+  return layout::size_of<T>();
+}
+
+template <typename T>
+std::size_t align_of() {
+  return layout::align_of<T>();
+}
+#endif // CXXBRIDGE1_LAYOUT
+
 #ifndef CXXBRIDGE1_RELOCATABLE
 #define CXXBRIDGE1_RELOCATABLE
 namespace detail {


Test patch:
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index cd367d85..e508494a 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -668,6 +668,11 @@ extern "C" const char *cxx_run_test() noexcept {
     }                                                                          \
   } while (false)
 
+  ASSERT(rust::size_of<R>() == sizeof(size_t));
+  ASSERT(rust::align_of<R>() == alignof(size_t));
+  ASSERT(rust::size_of<size_t>() == sizeof(size_t));
+  ASSERT(rust::align_of<size_t>() == alignof(size_t));
+
   ASSERT(r_return_primitive() == 2020);
   ASSERT(r_return_shared().z == 2020);
   ASSERT(cxx_test_suite_r_is_correct(&*r_return_box()));