Repository: dtolnay/cxx (language: rust)

Issue:
Add Missing C++ Annotations to Control Symbol Visibility
Hello David,

I have noticed that the flag `--cxx-impl-annotations` used on the command `cxxbridge` (from cxxbridge-cmd) does not seem to add annotations to all function and method declarations.

As a result of this problem I was only able to use cxx on Windows by compiling downstream libraries that linked to the Rust library by including the C++ source files. (You can see this in my (CMake based) https://github.com/david-cattermole/cxx-demo-example project. See the [main](https://github.com/david-cattermole/cxx-demo-example/tree/main) branch with the hack included.)

Since GCC and Clang default to exporting all symbols, but MSVC does not, I suspect this was not caught by other users. Or perhaps it's because I'm using `cxxbridge-cmd` directly, or maybe it's caused by older Windows compilers (Visual Studio 2015 / MSVC 14).

To fix the missing annotations I have made a commit to my cxx fork; https://github.com/david-cattermole/cxx/commit/363c18b2982329d6e4fbc2a6e5f324f4fef03661. The fix will add annotations to all the function (and method) declarations needed (in my libraries - I could be missing others).

You can see my cxx-demo-example project on the [shared-object-symbol-visibility](https://github.com/david-cattermole/cxx-demo-example/tree/shared-object-symbol-visibility) branch which uses my forked `cxx` repo. This has been tested on both Linux GCC and Windows MSVC and it appears to work (see output for [Linux/GCC](https://github.com/david-cattermole/cxx-demo-example/blob/shared-object-symbol-visibility/output_gcc_6_3_1.log) and [Windows/MSVC](https://github.com/david-cattermole/cxx-demo-example/blob/shared-object-symbol-visibility/output_msvc_19_0_24215_1.log)).

Would you like a PR? Or you can simply use my commit https://github.com/david-cattermole/cxx/commit/363c18b2982329d6e4fbc2a6e5f324f4fef03661 to implement a better fix?

Thanks,
David Cattermole


Discussion / hints:

Fix patch:
diff --git a/gen/src/write.rs b/gen/src/write.rs
index 4e802da8e..7a910f103 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -623,13 +623,17 @@ fn write_opaque_type_layout<'a>(out: &mut OutFile<'a>, ety: &'a ExternType) {
     writeln!(out, "}}");
 }
 
+fn begin_function_definition(out: &mut OutFile) {
+    if let Some(annotation) = &out.opt.cxx_impl_annotations {
+        write!(out, "{} ", annotation);
+    }
+}
+
 fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
     out.next_section();
     out.set_namespace(&efn.name.namespace);
     out.begin_block(Block::ExternC);
-    if let Some(annotation) = &out.opt.cxx_impl_annotations {
-        write!(out, "{} ", annotation);
-    }
+    begin_function_definition(out);
     if efn.throws {
         out.builtin.ptr_len = true;
         write!(out, "::rust::repr::PtrLen ");
@@ -946,6 +950,9 @@ fn write_rust_function_shim_impl(
         // We've already defined this inside the struct.
         return;
     }
+    if !out.header {
+        begin_function_definition(out);
+    }
     write_rust_function_shim_decl(out, local_name, sig, indirect_call);
     if out.header {
         writeln!(out, ";");
@@ -1483,6 +1490,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Pair) {
     let instance = ident.to_symbol();
 
     writeln!(out, "template <>");
+    begin_function_definition(out);
     writeln!(
         out,
         "{} *Box<{}>::allocation::alloc() noexcept {{",
@@ -1492,6 +1500,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Pair) {
     writeln!(out, "}}");
 
     writeln!(out, "template <>");
+    begin_function_definition(out);
     writeln!(
         out,
         "void Box<{}>::allocation::dealloc({} *ptr) noexcept {{",
@@ -1501,6 +1510,7 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Pair) {
     writeln!(out, "}}");
 
     writeln!(out, "template <>");
+    begin_function_definition(out);
     writeln!(out, "void Box<{}>::drop() noexcept {{", inner);
     writeln!(out, "  cxxbridge1$box${}$drop(this);", instance);
     writeln!(out, "}}");
@@ -1513,16 +1523,19 @@ fn write_rust_vec_impl(out: &mut OutFile, element: &RustName) {
     out.include.cstddef = true;
 
     writeln!(out, "template <>");
+    begin_function_definition(out);
     writeln!(out, "Vec<{}>::Vec() noexcept {{", inner);
     writeln!(out, "  cxxbridge1$rust_vec${}$new(this);", instance);
     writeln!(out, "}}");
 
     writeln!(out, "template <>");
+    begin_function_definition(out);
     writeln!(out, "void Vec<{}>::drop() noexcept {{", inner);
     writeln!(out, "  return cxxbridge1$rust_vec${}$drop(this);", instance);
     writeln!(out, "}}");
 
     writeln!(out, "template <>");
+    begin_function_definition(out);
     writeln!(
         out,
         "::std::size_t Vec<{}>::size() const noexcept {{",
@@ -1532,6 +1545,7 @@ fn write_rust_vec_impl(out: &mut OutFile, element: &RustName) {
     writeln!(out, "}}");
 
     writeln!(out, "template <>");
+    begin_function_definition(out);
     writeln!(
         out,
         "::std::size_t Vec<{}>::capacity() const noexcept {{",
@@ -1545,11 +1559,13 @@ fn write_rust_vec_impl(out: &mut OutFile, element: &RustName) {
     writeln!(out, "}}");
 
     writeln!(out, "template <>");
+    begin_function_definition(out);
     writeln!(out, "const {} *Vec<{0}>::data() const noexcept {{", inner);
     writeln!(out, "  return cxxbridge1$rust_vec${}$data(this);", instance);
     writeln!(out, "}}");
 
     writeln!(out, "template <>");
+    begin_function_definition(out);
     writeln!(
         out,
         "void Vec<{}>::reserve_total(::std::size_t cap) noexcept {{",
@@ -1563,6 +1579,7 @@ fn write_rust_vec_impl(out: &mut OutFile, element: &RustName) {
     writeln!(out, "}}");
 
     writeln!(out, "template <>");
+    begin_function_definition(out);
     writeln!(
         out,
         "void Vec<{}>::set_len(::std::size_t len) noexcept {{",


Test patch: