mirror of
https://gitee.com/openharmony/third_party_rust_cxx
synced 2025-02-10 00:32:39 +00:00
Support calling C++ methods from Rust
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: &C) -> usize; fn set(self: &mut C, n: usize); This syntax requires Rust 1.43. Note that the implementation also changes the internal naming of shim functions so that they also contain the name of the owning class, if any. This allows defining multiple methods with the same name on different objects.
This commit is contained in:
parent
4272d98530
commit
3d4f612b34
@ -9,12 +9,15 @@ ThingC::ThingC(std::string appname) : appname(std::move(appname)) {}
|
||||
|
||||
ThingC::~ThingC() { std::cout << "done with ThingC" << std::endl; }
|
||||
|
||||
const std::string &ThingC::get_name() const {
|
||||
std::cout << "I'm a C++ method!" << std::endl;
|
||||
return this->appname;
|
||||
}
|
||||
|
||||
std::unique_ptr<ThingC> make_demo(rust::Str appname) {
|
||||
return std::unique_ptr<ThingC>(new ThingC(std::string(appname)));
|
||||
}
|
||||
|
||||
const std::string &get_name(const ThingC &thing) { return thing.appname; }
|
||||
|
||||
void do_thing(SharedThing state) { print_r(*state.y); }
|
||||
|
||||
} // namespace example
|
||||
|
@ -12,12 +12,13 @@ public:
|
||||
~ThingC();
|
||||
|
||||
std::string appname;
|
||||
|
||||
const std::string &get_name() const;
|
||||
};
|
||||
|
||||
struct SharedThing;
|
||||
|
||||
std::unique_ptr<ThingC> make_demo(rust::Str appname);
|
||||
const std::string &get_name(const ThingC &thing);
|
||||
void do_thing(SharedThing state);
|
||||
|
||||
} // namespace example
|
||||
|
@ -11,8 +11,9 @@ mod ffi {
|
||||
|
||||
type ThingC;
|
||||
fn make_demo(appname: &str) -> UniquePtr<ThingC>;
|
||||
fn get_name(thing: &ThingC) -> &CxxString;
|
||||
fn get_name(self: &ThingC) -> &CxxString;
|
||||
fn do_thing(state: SharedThing);
|
||||
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
@ -29,7 +30,7 @@ fn print_r(r: &ThingR) {
|
||||
|
||||
fn main() {
|
||||
let x = ffi::make_demo("demo of cxx::bridge");
|
||||
println!("this is a {}", ffi::get_name(x.as_ref().unwrap()));
|
||||
println!("this is a {}", x.as_ref().unwrap().get_name());
|
||||
|
||||
ffi::do_thing(ffi::SharedThing {
|
||||
z: 222,
|
||||
|
29
gen/write.rs
29
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, Signature, Struct, Type, Types, Var};
|
||||
use crate::syntax::{Api, ExternFn, Receiver, Signature, Struct, Type, Types, Var};
|
||||
use proc_macro2::Ident;
|
||||
|
||||
pub(super) fn gen(
|
||||
@ -327,8 +327,11 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) {
|
||||
write_extern_return_type_space(out, &efn.ret, types);
|
||||
}
|
||||
write!(out, "{}cxxbridge02${}(", out.namespace, efn.ident);
|
||||
if let Some(base) = &efn.receiver {
|
||||
write!(out, "{} *__receiver$", base.ident);
|
||||
}
|
||||
for (i, arg) in efn.args.iter().enumerate() {
|
||||
if i > 0 {
|
||||
if i > 0 || efn.receiver.is_some() {
|
||||
write!(out, ", ");
|
||||
}
|
||||
if arg.ty == RustString {
|
||||
@ -347,14 +350,27 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) {
|
||||
writeln!(out, ") noexcept {{");
|
||||
write!(out, " ");
|
||||
write_return_type(out, &efn.ret);
|
||||
write!(out, "(*{}$)(", efn.ident);
|
||||
match &efn.receiver {
|
||||
None => write!(out, "(*{}$)(", efn.ident),
|
||||
Some(base) => write!(out, "({}::*{}$)(", base.ident, efn.ident),
|
||||
}
|
||||
for (i, arg) in efn.args.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(out, ", ");
|
||||
}
|
||||
write_type(out, &arg.ty);
|
||||
}
|
||||
writeln!(out, ") = {};", efn.ident);
|
||||
write!(out, ")");
|
||||
match &efn.receiver {
|
||||
Some(Receiver { mutability: None, ident: _ }) => write!(out, " const"),
|
||||
_ => {},
|
||||
}
|
||||
write!(out, " = ");
|
||||
match &efn.receiver {
|
||||
None => write!(out, "{}", efn.ident),
|
||||
Some(base) => write!(out, "&{}::{}", base.ident, efn.ident),
|
||||
}
|
||||
writeln!(out, ";");
|
||||
write!(out, " ");
|
||||
if efn.throws {
|
||||
writeln!(out, "::rust::Str::Repr throw$;");
|
||||
@ -377,7 +393,10 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) {
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
write!(out, "{}$(", efn.ident);
|
||||
match &efn.receiver {
|
||||
None => write!(out, "{}$(", efn.ident),
|
||||
Some(_) => write!(out, "(__receiver$->*{}$)(", efn.ident),
|
||||
}
|
||||
for (i, arg) in efn.args.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(out, ", ");
|
||||
|
@ -123,6 +123,13 @@ fn expand_cxx_type(ety: &ExternType) -> TokenStream {
|
||||
|
||||
fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
|
||||
let ident = &efn.ident;
|
||||
let receiver = efn.receiver.iter().map(|base| {
|
||||
let ident = &base.ident;
|
||||
match base.mutability {
|
||||
None => quote!(_: &#ident),
|
||||
Some(_) => quote!(_: &mut #ident),
|
||||
}
|
||||
});
|
||||
let args = efn.args.iter().map(|arg| {
|
||||
let ident = &arg.ident;
|
||||
let ty = expand_extern_type(&arg.ty);
|
||||
@ -136,6 +143,7 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types
|
||||
quote!(#ident: #ty)
|
||||
}
|
||||
});
|
||||
let all_args = receiver.chain(args);
|
||||
let ret = if efn.throws {
|
||||
quote!(-> ::cxx::private::Result)
|
||||
} else {
|
||||
@ -150,7 +158,7 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types
|
||||
let local_name = format_ident!("__{}", ident);
|
||||
quote! {
|
||||
#[link_name = #link_name]
|
||||
fn #local_name(#(#args,)* #outparam) #ret;
|
||||
fn #local_name(#(#all_args,)* #outparam) #ret;
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,7 +166,12 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types
|
||||
let ident = &efn.ident;
|
||||
let doc = &efn.doc;
|
||||
let decl = expand_cxx_function_decl(namespace, efn, types);
|
||||
let args = &efn.args;
|
||||
let receiver = efn.receiver.iter().map(|base| match base.mutability {
|
||||
None => quote!(&self),
|
||||
Some(_) => quote!(&mut self),
|
||||
});
|
||||
let args = efn.args.iter().map(|arg| quote!(#arg));
|
||||
let all_args = receiver.chain(args);
|
||||
let ret = if efn.throws {
|
||||
let ok = match &efn.ret {
|
||||
Some(ret) => quote!(#ret),
|
||||
@ -169,7 +182,8 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types
|
||||
expand_return_type(&efn.ret)
|
||||
};
|
||||
let indirect_return = indirect_return(efn, types);
|
||||
let vars = efn.args.iter().map(|arg| {
|
||||
let receiver_var = efn.receiver.iter().map(|_| quote!(self));
|
||||
let arg_vars = efn.args.iter().map(|arg| {
|
||||
let var = &arg.ident;
|
||||
match &arg.ty {
|
||||
Type::Ident(ident) if ident == RustString => {
|
||||
@ -189,6 +203,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types
|
||||
_ => quote!(#var),
|
||||
}
|
||||
});
|
||||
let vars = receiver_var.chain(arg_vars);
|
||||
let trampolines = efn
|
||||
.args
|
||||
.iter()
|
||||
@ -274,18 +289,36 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types
|
||||
})
|
||||
}
|
||||
.unwrap_or(call);
|
||||
quote! {
|
||||
#doc
|
||||
pub fn #ident(#args) #ret {
|
||||
extern "C" {
|
||||
#decl
|
||||
let receiver_ident = efn.receiver.as_ref().map(|base| &base.ident);
|
||||
match receiver_ident {
|
||||
None => quote! {
|
||||
#doc
|
||||
pub fn #ident(#(#all_args,)*) #ret {
|
||||
extern "C" {
|
||||
#decl
|
||||
}
|
||||
#trampolines
|
||||
unsafe {
|
||||
#setup
|
||||
#expr
|
||||
}
|
||||
}
|
||||
#trampolines
|
||||
unsafe {
|
||||
#setup
|
||||
#expr
|
||||
},
|
||||
Some(base_ident) => quote! {
|
||||
#doc
|
||||
impl #base_ident {
|
||||
pub fn #ident(#(#all_args,)*) #ret {
|
||||
extern "C" {
|
||||
#decl
|
||||
}
|
||||
#trampolines
|
||||
unsafe {
|
||||
#setup
|
||||
#expr
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,6 +195,10 @@ fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) {
|
||||
}
|
||||
}
|
||||
|
||||
if efn.receiver.is_some() {
|
||||
reference_args += 1;
|
||||
}
|
||||
|
||||
if reference_args != 1 {
|
||||
cx.error(
|
||||
efn,
|
||||
|
@ -49,6 +49,9 @@ pub mod ffi {
|
||||
fn c_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>;
|
||||
fn c_try_return_rust_string() -> Result<String>;
|
||||
fn c_try_return_unique_ptr_string() -> Result<UniquePtr<CxxString>>;
|
||||
|
||||
fn get(self: &C) -> usize;
|
||||
fn set(self: &mut C, n: usize) -> usize;
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
|
@ -15,6 +15,11 @@ C::C(size_t n) : n(n) {}
|
||||
|
||||
size_t C::get() const { return this->n; }
|
||||
|
||||
size_t C::set(size_t n) {
|
||||
this->n = n;
|
||||
return this->n;
|
||||
}
|
||||
|
||||
size_t c_return_primitive() { return 2020; }
|
||||
|
||||
Shared c_return_shared() { return Shared{2020}; }
|
||||
|
@ -12,6 +12,7 @@ class C {
|
||||
public:
|
||||
C(size_t n);
|
||||
size_t get() const;
|
||||
size_t set(size_t n);
|
||||
|
||||
private:
|
||||
size_t n;
|
||||
|
@ -107,6 +107,18 @@ fn test_c_call_r() {
|
||||
check!(cxx_run_test());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_c_method_calls() {
|
||||
let mut unique_ptr = ffi::c_return_unique_ptr();
|
||||
|
||||
let old_value = unique_ptr.as_ref().unwrap().get();
|
||||
assert_eq!(2020, old_value);
|
||||
assert_eq!(2021, unique_ptr.as_mut().unwrap().set(2021));
|
||||
assert_eq!(2021, unique_ptr.as_ref().unwrap().get());
|
||||
assert_eq!(old_value, unique_ptr.as_mut().unwrap().set(old_value));
|
||||
assert_eq!(old_value, unique_ptr.as_ref().unwrap().get())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn cxx_test_suite_get_box() -> *mut cxx_test_suite::R {
|
||||
Box::into_raw(Box::new(2020usize))
|
||||
|
Loading…
x
Reference in New Issue
Block a user