mirror of
https://github.com/topjohnwu/cxx.git
synced 2025-02-20 08:03:28 +00:00
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.
This commit is contained in:
parent
3d4f612b34
commit
c1c4e7ac6b
@ -18,7 +18,10 @@ std::unique_ptr<ThingC> make_demo(rust::Str appname) {
|
||||
return std::unique_ptr<ThingC>(new ThingC(std::string(appname)));
|
||||
}
|
||||
|
||||
void do_thing(SharedThing state) { print_r(*state.y); }
|
||||
void do_thing(SharedThing state) {
|
||||
print_r(*state.y);
|
||||
state.y->print();
|
||||
}
|
||||
|
||||
} // namespace example
|
||||
} // namespace org
|
||||
|
@ -19,6 +19,7 @@ mod ffi {
|
||||
extern "Rust" {
|
||||
type ThingR;
|
||||
fn print_r(r: &ThingR);
|
||||
fn print(self: &ThingR);
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,6 +29,12 @@ fn print_r(r: &ThingR) {
|
||||
println!("called back with r={}", r.0);
|
||||
}
|
||||
|
||||
impl ThingR {
|
||||
fn print(&self) {
|
||||
println!("method called back with r={}", self.0);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = ffi::make_demo("demo of cxx::bridge");
|
||||
println!("this is a {}", x.as_ref().unwrap().get_name());
|
||||
|
89
gen/write.rs
89
gen/write.rs
@ -2,7 +2,7 @@ use crate::gen::namespace::Namespace;
|
||||
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 @@ pub(super) fn gen(
|
||||
}
|
||||
|
||||
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 @@ fn write_struct_using(out: &mut OutFile, ident: &Ident) {
|
||||
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 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) {
|
||||
} 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_function_pointer_trampoline(
|
||||
}
|
||||
|
||||
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 @@ fn write_rust_function_decl_impl(
|
||||
}
|
||||
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 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) {
|
||||
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 @@ fn write_rust_function_shim_impl(
|
||||
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 @@ fn write_rust_function_shim_impl(
|
||||
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 {
|
||||
|
@ -154,7 +154,11 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types
|
||||
let ret = expand_extern_type(efn.ret.as_ref().unwrap());
|
||||
outparam = Some(quote!(__return: *mut #ret));
|
||||
}
|
||||
let link_name = format!("{}cxxbridge02${}", namespace, ident);
|
||||
let receiver_type = match &efn.receiver {
|
||||
Some(base) => base.ident.to_string(),
|
||||
None => "_".to_string(),
|
||||
};
|
||||
let link_name = format!("{}cxxbridge02${}${}", namespace, receiver_type, ident);
|
||||
let local_name = format_ident!("__{}", ident);
|
||||
quote! {
|
||||
#[link_name = #link_name]
|
||||
@ -366,7 +370,11 @@ fn expand_rust_type(ety: &ExternType) -> TokenStream {
|
||||
|
||||
fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
|
||||
let ident = &efn.ident;
|
||||
let link_name = format!("{}cxxbridge02${}", namespace, ident);
|
||||
let receiver_type = match &efn.receiver {
|
||||
Some(base) => base.ident.to_string(),
|
||||
None => "_".to_string(),
|
||||
};
|
||||
let link_name = format!("{}cxxbridge02${}${}", namespace, receiver_type, ident);
|
||||
let local_name = format_ident!("__{}", ident);
|
||||
let catch_unwind_label = format!("::{}", ident);
|
||||
let invoke = Some(ident);
|
||||
@ -388,6 +396,13 @@ fn expand_rust_function_shim_impl(
|
||||
catch_unwind_label: String,
|
||||
invoke: Option<&Ident>,
|
||||
) -> TokenStream {
|
||||
let receiver = sig.receiver.iter().map(|base| {
|
||||
let ident = &base.ident;
|
||||
match base.mutability {
|
||||
None => quote!(__receiver: &#ident),
|
||||
Some(_) => quote!(__receiver: &mut #ident),
|
||||
}
|
||||
});
|
||||
let args = sig.args.iter().map(|arg| {
|
||||
let ident = &arg.ident;
|
||||
let ty = expand_extern_type(&arg.ty);
|
||||
@ -397,6 +412,7 @@ fn expand_rust_function_shim_impl(
|
||||
quote!(#ident: #ty)
|
||||
}
|
||||
});
|
||||
let all_args = receiver.chain(args);
|
||||
|
||||
let vars = sig.args.iter().map(|arg| {
|
||||
let ident = &arg.ident;
|
||||
@ -418,7 +434,10 @@ fn expand_rust_function_shim_impl(
|
||||
});
|
||||
|
||||
let mut call = match invoke {
|
||||
Some(ident) => quote!(super::#ident),
|
||||
Some(ident) => match sig.receiver {
|
||||
None => quote!(super::#ident),
|
||||
Some(_) => quote!(__receiver.#ident),
|
||||
},
|
||||
None => quote!(__extern),
|
||||
};
|
||||
call.extend(quote! { (#(#vars),*) });
|
||||
@ -476,7 +495,7 @@ fn expand_rust_function_shim_impl(
|
||||
quote! {
|
||||
#[doc(hidden)]
|
||||
#[export_name = #link_name]
|
||||
unsafe extern "C" fn #local_name(#(#args,)* #outparam #pointer) #ret {
|
||||
unsafe extern "C" fn #local_name(#(#all_args,)* #outparam #pointer) #ret {
|
||||
let __fn = concat!(module_path!(), #catch_unwind_label);
|
||||
#expr
|
||||
}
|
||||
|
@ -56,6 +56,7 @@ pub mod ffi {
|
||||
|
||||
extern "Rust" {
|
||||
type R;
|
||||
type R2;
|
||||
|
||||
fn r_return_primitive() -> usize;
|
||||
fn r_return_shared() -> Shared;
|
||||
@ -80,11 +81,28 @@ pub mod ffi {
|
||||
fn r_try_return_void() -> Result<()>;
|
||||
fn r_try_return_primitive() -> Result<usize>;
|
||||
fn r_fail_return_primitive() -> Result<usize>;
|
||||
|
||||
fn r_return_r2(n: usize) -> Box<R2>;
|
||||
fn get(self: &R2) -> usize;
|
||||
fn set(self: &mut R2, n: usize) -> usize;
|
||||
}
|
||||
}
|
||||
|
||||
pub type R = usize;
|
||||
|
||||
pub struct R2(usize);
|
||||
|
||||
impl R2 {
|
||||
fn get(&self) -> usize {
|
||||
self.0
|
||||
}
|
||||
|
||||
fn set(&mut self, n: usize) -> usize {
|
||||
self.0 = n;
|
||||
n
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Error;
|
||||
|
||||
@ -187,3 +205,7 @@ fn r_try_return_primitive() -> Result<usize, Error> {
|
||||
fn r_fail_return_primitive() -> Result<usize, Error> {
|
||||
Err(Error)
|
||||
}
|
||||
|
||||
fn r_return_r2(n: usize) -> Box<R2> {
|
||||
Box::new(R2(n))
|
||||
}
|
||||
|
@ -181,6 +181,13 @@ extern "C" const char *cxx_run_test() noexcept {
|
||||
ASSERT(std::strcmp(e.what(), "rust error") == 0);
|
||||
}
|
||||
|
||||
auto r2 = r_return_r2(2020);
|
||||
ASSERT(r2->get() == 2020);
|
||||
ASSERT(r2->set(2021) == 2021);
|
||||
ASSERT(r2->get() == 2021);
|
||||
ASSERT(r2->set(2020) == 2020);
|
||||
ASSERT(r2->get() == 2020);
|
||||
|
||||
cxx_test_suite_set_correct();
|
||||
return nullptr;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user