Merge pull request #437 from dtolnay/methods

Support methods/member functions on shared structs
diff --git a/gen/src/write.rs b/gen/src/write.rs
index b8d1dd4..39053a1 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -66,7 +66,7 @@
 fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) {
     let mut methods_for_type = HashMap::new();
     for api in apis {
-        if let Api::RustFunction(efn) = api {
+        if let Api::CxxFunction(efn) | Api::RustFunction(efn) = api {
             if let Some(receiver) = &efn.sig.receiver {
                 methods_for_type
                     .entry(&receiver.ty.rust)
@@ -84,7 +84,11 @@
                 for next in &mut toposorted_structs {
                     if !out.types.cxx.contains(&strct.name.rust) {
                         out.next_section();
-                        write_struct(out, next);
+                        let methods = methods_for_type
+                            .get(&strct.name.rust)
+                            .map(Vec::as_slice)
+                            .unwrap_or_default();
+                        write_struct(out, next, methods);
                     }
                     structs_written.insert(&next.name.rust);
                     if next.name.rust == strct.name.rust {
@@ -173,7 +177,7 @@
     }
 }
 
-fn write_struct<'a>(out: &mut OutFile<'a>, strct: &'a Struct) {
+fn write_struct<'a>(out: &mut OutFile<'a>, strct: &'a Struct, methods: &[&ExternFn]) {
     out.set_namespace(&strct.name.namespace);
     let guard = format!("CXXBRIDGE05_STRUCT_{}", strct.name.to_symbol());
     writeln!(out, "#ifndef {}", guard);
@@ -187,6 +191,16 @@
         write_type_space(out, &field.ty);
         writeln!(out, "{};", field.ident);
     }
+    if !methods.is_empty() {
+        writeln!(out);
+    }
+    for method in methods {
+        write!(out, "  ");
+        let sig = &method.sig;
+        let local_name = method.name.cxx.to_string();
+        write_rust_function_shim_decl(out, &local_name, sig, false);
+        writeln!(out, ";");
+    }
     writeln!(out, "}};");
     writeln!(out, "#endif // {}", guard);
 }
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index 9ea3450..96f9b07 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -202,6 +202,7 @@
         fn set2(&mut self, n: usize) -> usize;
         fn set_succeed(&mut self, n: usize) -> Result<usize>;
         fn get_fail(&mut self) -> Result<usize>;
+        fn c_method_on_shared(self: &Shared) -> usize;
 
         #[rust_name = "i32_overloaded_method"]
         fn cOverloadedMethod(&self, x: i32) -> String;
@@ -273,6 +274,7 @@
         fn r_return_r2(n: usize) -> Box<R2>;
         fn get(self: &R2) -> usize;
         fn set(self: &mut R2, n: usize) -> usize;
+        fn r_method_on_shared(self: &Shared) -> usize;
 
         #[cxx_name = "rAliasedFunction"]
         fn r_aliased_function(x: i32) -> String;
@@ -315,6 +317,12 @@
     }
 }
 
+impl ffi::Shared {
+    fn r_method_on_shared(&self) -> usize {
+        2020
+    }
+}
+
 #[derive(Debug)]
 struct Error;
 
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index 747788f..fd4751e 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -35,6 +35,8 @@
 
 size_t C::get_fail() { throw std::runtime_error("unimplemented"); }
 
+size_t Shared::c_method_on_shared() const noexcept { return 2021; }
+
 const std::vector<uint8_t> &C::get_v() const { return this->v; }
 
 std::vector<uint8_t> &C::get_v() { return this->v; }
@@ -624,6 +626,7 @@
   ASSERT(r2->get() == 2021);
   ASSERT(r2->set(2020) == 2020);
   ASSERT(r2->get() == 2020);
+  ASSERT(Shared{0}.r_method_on_shared() == 2020);
 
   ASSERT(std::string(rAliasedFunction(2020)) == "2020");
 
diff --git a/tests/test.rs b/tests/test.rs
index e3f9fce..a2c64c3 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -193,6 +193,7 @@
     assert_eq!(old_value, unique_ptr.get2());
     assert_eq!(2022, unique_ptr.set_succeed(2022).unwrap());
     assert!(unique_ptr.get_fail().is_err());
+    assert_eq!(2021, ffi::Shared { z: 0 }.c_method_on_shared());
 }
 
 #[test]