Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cxx_name attribute - function aliases, import/export static methods, … #220

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 43 additions & 9 deletions gen/src/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::gen::{include, Opt};
use crate::syntax::atom::Atom::{self, *};
use crate::syntax::namespace::Namespace;
use crate::syntax::symbol::Symbol;
use crate::syntax::{mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, Var};
use crate::syntax::{mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, Var, CxxSide};
use proc_macro2::Ident;
use std::collections::HashMap;

Expand Down Expand Up @@ -54,6 +54,11 @@ pub(super) fn gen(
.entry(&receiver.ty)
.or_insert_with(Vec::new)
.push(efn);
} else if let Some(CxxSide { class: Some(class @ _), .. }) = &efn.cxx_side {
methods_for_type
.entry(class)
.or_insert_with(Vec::new)
.push(efn);
}
}
}
Expand Down Expand Up @@ -342,7 +347,15 @@ fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&Ex
for method in methods {
write!(out, " ");
let sig = &method.sig;
let local_name = method.ident.to_string();
let mut local_name = method.ident.to_string();
if let Some(cxx_side) = &method.cxx_side {
if cxx_side.class.is_some() {
write!(out, "static ");
}
if let Some(name) = &cxx_side.name {
local_name = name.to_string();
}
}
write_rust_function_shim_decl(out, &local_name, sig, false);
writeln!(out, ";");
}
Expand Down Expand Up @@ -450,9 +463,17 @@ fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) {
}
}
write!(out, " = ");
match &efn.receiver {
None => write!(out, "{}", efn.ident),
Some(receiver) => write!(out, "&{}::{}", receiver.ty, efn.ident),
let name = efn.cxx_side
.as_ref()
.and_then(|s| s.name.as_ref())
.unwrap_or_else(|| &efn.ident);
if let Some(CxxSide { class: Some(class @ _), .. }) = &efn.cxx_side {
write!(out, "&{}::{}", class, name);
} else {
match &efn.receiver {
None => write!(out, "{}", name),
Some(receiver) => write!(out, "&{}::{}", receiver.ty, name),
}
}
writeln!(out, ";");
write!(out, " ");
Expand Down Expand Up @@ -612,9 +633,22 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) {
for line in efn.doc.to_string().lines() {
writeln!(out, "//{}", line);
}
let local_name = match &efn.sig.receiver {
None => efn.ident.to_string(),
Some(receiver) => format!("{}::{}", receiver.ty, efn.ident),
let local_name = if let Some(cxx_side) = &efn.cxx_side {
let name = cxx_side.name
.as_ref()
.unwrap_or_else(|| &efn.ident);
match &efn.sig.receiver {
None => cxx_side.class
.as_ref()
.map(|c| format!("{}::{}", c, name))
.unwrap_or_else(|| name.to_string()),
Some(receiver) => format!("{}::{}", receiver.ty, name),
}
} else {
match &efn.sig.receiver {
None => efn.ident.to_string(),
Some(receiver) => format!("{}::{}", receiver.ty, efn.ident),
}
};
let invoke = mangle::extern_fn(&out.namespace, efn);
let indirect_call = false;
Expand Down Expand Up @@ -661,7 +695,7 @@ fn write_rust_function_shim_impl(
invoke: &Symbol,
indirect_call: bool,
) {
if out.header && sig.receiver.is_some() {
if out.header && (sig.receiver.is_some() || local_name.contains("::")) {
// We've already defined this inside the struct.
return;
}
Expand Down
15 changes: 13 additions & 2 deletions macro/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,9 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types
}
}
};
if let Some(crate::syntax::CxxSide { class: Some(class @ _), .. }) = &efn.cxx_side {
return quote!(impl #class { #function_shim });
}
match &efn.receiver {
None => function_shim,
Some(receiver) => {
Expand All @@ -396,6 +399,7 @@ fn expand_function_pointer_trampoline(
let local_name = parse_quote!(__);
let catch_unwind_label = format!("::{}::{}", efn.ident, var);
let shim = expand_rust_function_shim_impl(
efn,
sig,
types,
&r_trampoline,
Expand Down Expand Up @@ -433,6 +437,7 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type
let catch_unwind_label = format!("::{}", ident);
let invoke = Some(ident);
expand_rust_function_shim_impl(
efn,
efn,
types,
&link_name,
Expand All @@ -443,6 +448,7 @@ fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Type
}

fn expand_rust_function_shim_impl(
efn: &ExternFn,
sig: &Signature,
types: &Types,
link_name: &Symbol,
Expand Down Expand Up @@ -490,10 +496,15 @@ fn expand_rust_function_shim_impl(
}
});
let vars = receiver_var.into_iter().chain(arg_vars);

let mut call = match invoke {
Some(ident) => match &sig.receiver {
None => quote!(super::#ident),
None => {
efn.cxx_side
.as_ref()
.and_then(|s| s.class.as_ref())
.map(|class| quote!(#class::#ident))
.unwrap_or_else(|| quote!(super::#ident))
}
Some(receiver) => {
let receiver_type = &receiver.ty;
quote!(#receiver_type::#ident)
Expand Down
16 changes: 14 additions & 2 deletions syntax/attrs.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::syntax::report::Errors;
use crate::syntax::Atom::{self, *};
use crate::syntax::{Derive, Doc};
use crate::syntax::{Derive, Doc, CxxSide};
use proc_macro2::Ident;
use syn::parse::{ParseStream, Parser as _};
use syn::{Attribute, Error, LitStr, Path, Result, Token};
Expand All @@ -10,6 +10,7 @@ pub struct Parser<'a> {
pub doc: Option<&'a mut Doc>,
pub derives: Option<&'a mut Vec<Derive>>,
pub repr: Option<&'a mut Option<Atom>>,
pub cxx_side: Option<&'a mut Option<CxxSide>>,
}

pub(super) fn parse_doc(cx: &mut Errors, attrs: &[Attribute]) -> Doc {
Expand Down Expand Up @@ -57,8 +58,19 @@ pub(super) fn parse(cx: &mut Errors, attrs: &[Attribute], mut parser: Parser) {
}
Err(err) => return cx.push(err),
}
} else if attr.path.is_ident("cxx_name") {
match (|input: ParseStream| input.parse::<CxxSide>()).parse2(attr.tokens.clone()) {
Ok(attr) => {
if let Some(cxx_side) = &mut parser.cxx_side {
**cxx_side = Some(attr);
continue;
}
}
Err(err) => return cx.push(err),
}
} else {
return cx.error(attr, "unsupported attribute");
}
return cx.error(attr, "unsupported attribute");
}
}

Expand Down
23 changes: 22 additions & 1 deletion syntax/impls.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::syntax::{ExternFn, Receiver, Ref, Signature, Slice, Ty1, Type};
use crate::syntax::{ExternFn, Receiver, Ref, Signature, Slice, Ty1, Type, CxxSide};
use std::hash::{Hash, Hasher};
use std::mem;
use std::ops::{Deref, DerefMut};
use syn::{LitStr, Error, Token, Path};
use syn::parse::{Parse, ParseStream, Result};

impl Deref for ExternFn {
type Target = Signature;
Expand All @@ -17,6 +19,25 @@ impl DerefMut for ExternFn {
}
}

impl Parse for CxxSide {
fn parse(input: ParseStream) -> Result<Self> {
input.parse::<Token![=]>()?;
let begin = input.cursor();
let name = input.parse::<LitStr>()?.parse_with(Path::parse_mod_style)?;
match name.segments.len() {
2 => Ok(CxxSide {
name: name.segments.last().map(|p| p.ident.clone()),
class: name.segments.first().map(|p| p.ident.clone()),
}),
1 => Ok(CxxSide {
name: name.segments.first().map(|p| p.ident.clone()),
class: None,
}),
_ => Err(Error::new(begin.span(), "incorrect name")),
}
}
}

impl Hash for Type {
fn hash<H: Hasher>(&self, state: &mut H) {
mem::discriminant(self).hash(state);
Expand Down
7 changes: 7 additions & 0 deletions syntax/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,17 @@ pub struct ExternFn {
pub lang: Lang,
pub doc: Doc,
pub ident: Ident,
pub cxx_side: Option<CxxSide>,
pub sig: Signature,
pub semi_token: Token![;],
}

#[derive(Debug, Default)]
pub struct CxxSide {
pub name: Option<Ident>,
pub class: Option<Ident>,
}

pub struct TypeAlias {
pub type_token: Token![type],
pub ident: Ident,
Expand Down
5 changes: 4 additions & 1 deletion syntax/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use syn::{

pub mod kw {
syn::custom_keyword!(Result);
syn::custom_keyword!(name);
}

pub fn parse_items(cx: &mut Errors, items: Vec<Item>) -> Vec<Api> {
Expand Down Expand Up @@ -333,11 +334,13 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R
Lang::Cxx => Api::CxxFunction,
Lang::Rust => Api::RustFunction,
};

let mut cxx_side = None;
attrs::parse(cx, &foreign_fn.attrs, attrs::Parser { cxx_side: Some(&mut cxx_side), ..Default::default() });
Ok(api_function(ExternFn {
lang,
doc,
ident,
cxx_side,
sig: Signature {
fn_token,
receiver,
Expand Down
27 changes: 27 additions & 0 deletions tests/ffi/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,19 @@ pub mod ffi {
fn set2(&mut self, n: usize) -> usize;
fn set_succeed(&mut self, n: usize) -> Result<usize>;
fn get_fail(&mut self) -> Result<usize>;

#[cxx_name = "cOverloadedMethod"]
fn i32_overloaded_method(&self, x: i32) -> String;
#[cxx_name = "cOverloadedMethod"]
fn str_overloaded_method(&self, x: &str) -> String;

#[cxx_name = "cOverloadedFunction"]
fn i32_overloaded_function(x: i32) -> String;
#[cxx_name = "cOverloadedFunction"]
fn str_overloaded_function(x: &str) -> String;

#[cxx_name = "C::cStaticMethod"]
fn static_method() -> String;
}

extern "C" {
Expand Down Expand Up @@ -136,6 +149,12 @@ pub mod ffi {
fn r_return_r2(n: usize) -> Box<R2>;
fn get(self: &R2) -> usize;
fn set(self: &mut R2, n: usize) -> usize;

#[cxx_name = "rAliasedFunction"]
fn r_aliased_function(x: i32) -> String;

#[cxx_name = "R2::rStaticMethod"]
fn r_static_method() -> String;
}
}

Expand All @@ -152,6 +171,10 @@ impl R2 {
self.0 = n;
n
}

fn r_static_method() -> String {
"Rust static method".into()
}
}

#[derive(Debug)]
Expand Down Expand Up @@ -299,3 +322,7 @@ fn r_fail_return_primitive() -> Result<usize, Error> {
fn r_return_r2(n: usize) -> Box<R2> {
Box::new(R2(n))
}

fn r_aliased_function(x: i32) -> String {
x.to_string()
}
24 changes: 24 additions & 0 deletions tests/ffi/tests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,26 @@ extern "C" std::string *cxx_test_suite_get_unique_ptr_string() noexcept {
return std::unique_ptr<std::string>(new std::string("2020")).release();
}

rust::String C::cOverloadedMethod(int32_t x) const {
return rust::String(std::to_string(x));
}

rust::String C::cOverloadedMethod(rust::Str x) const {
return rust::String(std::string(x));
}

rust::String C::cStaticMethod() {
return rust::String("C/C++ static method");
}

rust::String cOverloadedFunction(int x) {
return rust::String(std::to_string(x));
}

rust::String cOverloadedFunction(rust::Str x) {
return rust::String(std::string(x));
}

extern "C" const char *cxx_run_test() noexcept {
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
Expand Down Expand Up @@ -346,6 +366,10 @@ extern "C" const char *cxx_run_test() noexcept {
ASSERT(r2->get() == 2021);
ASSERT(r2->set(2020) == 2020);
ASSERT(r2->get() == 2020);

ASSERT(std::string(rAliasedFunction(2020)) == "2020");

ASSERT(std::string(R2::rStaticMethod()) == "Rust static method");

cxx_test_suite_set_correct();
return nullptr;
Expand Down
6 changes: 6 additions & 0 deletions tests/ffi/tests.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ class C {
size_t set_succeed(size_t n);
size_t get_fail();
const std::vector<uint8_t> &get_v() const;
rust::String cOverloadedMethod(int32_t x) const;
rust::String cOverloadedMethod(rust::Str x) const;
static rust::String cStaticMethod();

private:
size_t n;
Expand Down Expand Up @@ -84,4 +87,7 @@ std::unique_ptr<std::string> c_try_return_unique_ptr_string();
rust::Vec<uint8_t> c_try_return_rust_vec();
const rust::Vec<uint8_t> &c_try_return_ref_rust_vec(const C &c);

rust::String cOverloadedFunction(int32_t x);
rust::String cOverloadedFunction(rust::Str x);

} // namespace tests
11 changes: 11 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,14 @@ extern "C" fn cxx_test_suite_get_box() -> *mut cxx_test_suite::R {
unsafe extern "C" fn cxx_test_suite_r_is_correct(r: *const cxx_test_suite::R) -> bool {
*r == 2020
}

#[test]
fn test_cxx_side_attribute() {
assert_eq!("2020".to_string(), ffi::i32_overloaded_function(2020));
assert_eq!("2020".to_string(), ffi::str_overloaded_function("2020"));
let unique_ptr = ffi::c_return_unique_ptr();
assert_eq!("2020".to_string(), unique_ptr.i32_overloaded_method(2020));
assert_eq!("2020".to_string(), unique_ptr.str_overloaded_method("2020"));

assert_eq!("C/C++ static method".to_string(), ffi::C::static_method());
}