Support calling Rust methods from C++
These methods can be declared in the bridge by naming the first
argument self and making it a reference to the containing class, e.g.,
fn get(self: &R) -> usize;
fn set(self: &mut R, n: usize);
This syntax requires Rust 1.43.
diff --git a/gen/write.rs b/gen/write.rs
index 41b5b37..7e1caac 100644
--- a/gen/write.rs
+++ b/gen/write.rs
@@ -2,7 +2,7 @@
use crate::gen::out::OutFile;
use crate::gen::{include, Opt};
use crate::syntax::atom::Atom::{self, *};
-use crate::syntax::{Api, ExternFn, Receiver, Signature, Struct, Type, Types, Var};
+use crate::syntax::{Api, ExternFn, ExternType, Receiver, Signature, Struct, Type, Types, Var};
use proc_macro2::Ident;
pub(super) fn gen(
@@ -45,9 +45,25 @@
}
for api in apis {
- if let Api::Struct(strct) = api {
- out.next_section();
- write_struct(out, strct);
+ match api {
+ Api::Struct(strct) => {
+ out.next_section();
+ write_struct(out, strct);
+ }
+ Api::RustType(ety) => {
+ let methods = apis.iter().filter_map(|api| match api {
+ Api::RustFunction(efn) => match &efn.sig.receiver {
+ Some(rcvr) if rcvr.ident == ety.ident => Some(efn),
+ _ => None,
+ },
+ _ => None,
+ }).collect::<Vec<_>>();
+ if !methods.is_empty() {
+ out.next_section();
+ write_struct_with_methods(out, ety, methods);
+ }
+ }
+ _ => {}
}
}
@@ -300,6 +316,21 @@
writeln!(out, "using {} = {};", ident, ident);
}
+fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: Vec<&ExternFn>) {
+ for line in ety.doc.to_string().lines() {
+ writeln!(out, "//{}", line);
+ }
+ writeln!(out, "struct {} final {{", ety.ident);
+ for method in &methods {
+ write!(out, " ");
+ let sig = &method.sig;
+ let local_name = method.ident.to_string();
+ write_rust_function_shim_decl(out, &local_name, sig, None, false);
+ writeln!(out, ";");
+ }
+ writeln!(out, "}};");
+}
+
fn write_exception_glue(out: &mut OutFile, apis: &[Api]) {
let mut has_cxx_throws = false;
for api in apis {
@@ -326,7 +357,11 @@
} else {
write_extern_return_type_space(out, &efn.ret, types);
}
- write!(out, "{}cxxbridge02${}(", out.namespace, efn.ident);
+ let receiver_type = match &efn.receiver {
+ Some(base) => base.ident.to_string(),
+ None => "_".to_string(),
+ };
+ write!(out, "{}cxxbridge02${}${}(", out.namespace, receiver_type, efn.ident);
if let Some(base) = &efn.receiver {
write!(out, "{} *__receiver$", base.ident);
}
@@ -471,7 +506,11 @@
}
fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) {
- let link_name = format!("{}cxxbridge02${}", out.namespace, efn.ident);
+ let receiver_type = match &efn.receiver {
+ Some(base) => base.ident.to_string(),
+ None => "_".to_string(),
+ };
+ let link_name = format!("{}cxxbridge02${}${}", out.namespace, receiver_type, efn.ident);
let indirect_call = false;
write_rust_function_decl_impl(out, &link_name, efn, types, indirect_call);
}
@@ -490,6 +529,10 @@
}
write!(out, "{}(", link_name);
let mut needs_comma = false;
+ if let Some(base) = &sig.receiver {
+ write!(out, "{} &__receiver$", base.ident);
+ needs_comma = true;
+ }
for arg in &sig.args {
if needs_comma {
write!(out, ", ");
@@ -519,20 +562,26 @@
writeln!(out, "//{}", line);
}
let local_name = efn.ident.to_string();
- let invoke = format!("{}cxxbridge02${}", out.namespace, efn.ident);
+ let receiver_type = match &efn.receiver {
+ Some(base) => base.ident.to_string(),
+ None => "_".to_string(),
+ };
+ let invoke = format!("{}cxxbridge02${}${}", out.namespace, receiver_type, efn.ident);
let indirect_call = false;
write_rust_function_shim_impl(out, &local_name, efn, types, &invoke, indirect_call);
}
-fn write_rust_function_shim_impl(
+fn write_rust_function_shim_decl(
out: &mut OutFile,
local_name: &str,
sig: &Signature,
- types: &Types,
- invoke: &str,
+ receiver: Option<&Receiver>,
indirect_call: bool,
) {
write_return_type(out, &sig.ret);
+ if let Some(base) = receiver {
+ write!(out, "{}::", base.ident);
+ }
write!(out, "{}(", local_name);
for (i, arg) in sig.args.iter().enumerate() {
if i > 0 {
@@ -551,6 +600,21 @@
if !sig.throws {
write!(out, " noexcept");
}
+}
+
+fn write_rust_function_shim_impl(
+ out: &mut OutFile,
+ local_name: &str,
+ sig: &Signature,
+ types: &Types,
+ invoke: &str,
+ indirect_call: bool,
+) {
+ if out.header && sig.receiver.is_some() {
+ // We've already defined this inside the struct.
+ return;
+ }
+ write_rust_function_shim_decl(out, local_name, sig, sig.receiver.as_ref(), indirect_call);
if out.header {
writeln!(out, ";");
} else {
@@ -589,8 +653,11 @@
write!(out, "::rust::Str::Repr error$ = ");
}
write!(out, "{}(", invoke);
+ if let Some(_) = &sig.receiver {
+ write!(out, "*this");
+ }
for (i, arg) in sig.args.iter().enumerate() {
- if i > 0 {
+ if i > 0 || sig.receiver.is_some() {
write!(out, ", ");
}
match &arg.ty {