Skip to content

Commit

Permalink
Merge pull request #121 from jgalenson/methods
Browse files Browse the repository at this point in the history
Support calling C++ and Rust methods
  • Loading branch information
dtolnay authored Apr 17, 2020
2 parents 4272d98 + 187588e commit af74d66
Show file tree
Hide file tree
Showing 13 changed files with 271 additions and 42 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ anyhow = "1.0"
cc = "1.0.49"
codespan-reporting = "0.9"
cxxbridge-macro = { version = "=0.2.9", path = "macro" }
itertools = "0.9"
link-cplusplus = "1.0"
proc-macro2 = { version = "1.0", features = ["span-locations"] }
quote = "1.0"
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,10 @@ mod ffi {

// Functions implemented in C++.
fn make_demo(appname: &str) -> UniquePtr<ThingC>;
fn get_name(thing: &ThingC) -> &CxxString;
fn do_thing(state: SharedThing);

// Methods implemented in C++.
fn get_name(self: &ThingC) -> &CxxString;
}

extern "Rust" {
Expand All @@ -95,6 +97,9 @@ mod ffi {

// Functions implemented in Rust.
fn print_r(r: &ThingR);

// Methods implemented in Rust.
fn print(self: &ThingR);
}
}
```
Expand Down Expand Up @@ -335,8 +340,6 @@ This is still early days for CXX; I am releasing it as a minimum viable product
to collect feedback on the direction and invite collaborators. Here are some of
the facets that I still intend for this project to tackle:

- [ ] Support associated methods: `extern "Rust" { fn f(self: &Struct); }`
- [ ] Support C++ member functions
- [ ] Support structs with type parameters
- [ ] Support async functions

Expand Down
12 changes: 9 additions & 3 deletions demo-cxx/demo.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@ 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); }
void do_thing(SharedThing state) {
print_r(*state.y);
state.y->print();
}

} // namespace example
} // namespace org
3 changes: 2 additions & 1 deletion demo-cxx/demo.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ class ThingC {
~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
Expand Down
12 changes: 10 additions & 2 deletions demo-rs/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ 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" {
type ThingR;
fn print_r(r: &ThingR);
fn print(self: &ThingR);
}
}

Expand All @@ -27,9 +29,15 @@ 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 {}", ffi::get_name(x.as_ref().unwrap()));
println!("this is a {}", x.as_ref().unwrap().get_name());

ffi::do_thing(ffi::SharedThing {
z: 222,
Expand Down
123 changes: 108 additions & 15 deletions gen/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ 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, ExternType, Receiver, Signature, Struct, Type, Types, Var};
use itertools::Itertools;
use proc_macro2::Ident;

pub(super) fn gen(
Expand Down Expand Up @@ -44,10 +45,30 @@ pub(super) fn gen(
}
}

let methods_for_type = apis.iter().filter_map(|api| match api {
Api::RustFunction(efn) => match &efn.sig.receiver {
Some(rcvr) => Some((&rcvr.ident, efn)),
_ => None,
},
_ => None,
}).into_group_map();

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) => {
match methods_for_type.get(&ety.ident) {
Some(methods) => {
out.next_section();
write_struct_with_methods(out, ety, methods);
},
_ => {}
}
}
_ => {}
}
}

Expand Down Expand Up @@ -300,6 +321,23 @@ 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);
writeln!(out, " {}() = delete;", ety.ident);
writeln!(out, " {}(const {}&) = delete;", ety.ident, 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 {
Expand All @@ -326,9 +364,16 @@ 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);
}
for (i, arg) in efn.args.iter().enumerate() {
if i > 0 {
if i > 0 || efn.receiver.is_some() {
write!(out, ", ");
}
if arg.ty == RustString {
Expand All @@ -347,14 +392,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$;");
Expand All @@ -377,7 +435,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, ", ");
Expand Down Expand Up @@ -452,7 +513,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);
}
Expand All @@ -471,6 +536,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, ", ");
Expand Down Expand Up @@ -500,20 +569,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 {
Expand All @@ -532,6 +607,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 {
Expand Down Expand Up @@ -570,8 +660,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 {
Expand Down
Loading

0 comments on commit af74d66

Please sign in to comment.