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

WIP: cxx-qt-gen: use original bridge definition in CXX and remove camel/snake conversion #667

Closed
wants to merge 4 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
30 changes: 24 additions & 6 deletions crates/cxx-qt-gen/src/generator/cpp/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,10 @@ mod tests {
fn test_generate_cpp_invokables() {
let invokables = vec![
ParsedMethod {
method: parse_quote! { fn void_invokable(self: &MyObject); },
method: parse_quote! {
#[cxx_name = "voidInvokable"]
fn void_invokable(self: &MyObject);
},
qobject_ident: format_ident!("MyObject"),
mutable: false,
safe: true,
Expand All @@ -178,7 +181,10 @@ mod tests {
is_qinvokable: true,
},
ParsedMethod {
method: parse_quote! { fn trivial_invokable(self: &MyObject, param: i32) -> i32; },
method: parse_quote! {
#[cxx_name = "trivialInvokable"]
fn trivial_invokable(self: &MyObject, param: i32) -> i32;
},
qobject_ident: format_ident!("MyObject"),
mutable: false,
safe: true,
Expand All @@ -190,7 +196,10 @@ mod tests {
is_qinvokable: true,
},
ParsedMethod {
method: parse_quote! { fn opaque_invokable(self: Pin<&mut MyObject>, param: &QColor) -> UniquePtr<QColor>; },
method: parse_quote! {
#[cxx_name = "opaqueInvokable"]
fn opaque_invokable(self: Pin<&mut MyObject>, param: &QColor) -> UniquePtr<QColor>;
},
qobject_ident: format_ident!("MyObject"),
mutable: true,
safe: true,
Expand All @@ -202,7 +211,10 @@ mod tests {
is_qinvokable: true,
},
ParsedMethod {
method: parse_quote! { fn specifiers_invokable(self: &MyObject, param: i32) -> i32; },
method: parse_quote! {
#[cxx_name = "specifiersInvokable"]
fn specifiers_invokable(self: &MyObject, param: i32) -> i32;
},
qobject_ident: format_ident!("MyObject"),
mutable: false,
safe: true,
Expand All @@ -220,7 +232,10 @@ mod tests {
is_qinvokable: true,
},
ParsedMethod {
method: parse_quote! { fn cpp_method(self: &MyObject); },
method: parse_quote! {
#[cxx_name = "cppMethod"]
fn cpp_method(self: &MyObject);
},
qobject_ident: format_ident!("MyObject"),
mutable: false,
safe: true,
Expand Down Expand Up @@ -388,7 +403,10 @@ mod tests {
#[test]
fn test_generate_cpp_invokables_mapped_cxx_name() {
let invokables = vec![ParsedMethod {
method: parse_quote! { fn trivial_invokable(self: &MyObject, param: A) -> B; },
method: parse_quote! {
#[cxx_name = "trivialInvokable"]
fn trivial_invokable(self: &MyObject, param: A) -> B;
},
qobject_ident: format_ident!("MyObject"),
mutable: false,
safe: true,
Expand Down
72 changes: 56 additions & 16 deletions crates/cxx-qt-gen/src/generator/naming/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,80 @@

// SPDX-License-Identifier: MIT OR Apache-2.0

use convert_case::{Case, Casing};
use quote::format_ident;
use syn::Ident;
use syn::{Attribute, Ident};

use crate::syntax::{attribute::attribute_find_path, expr::expr_to_string};

use super::CombinedIdent;

impl CombinedIdent {
/// Generate a CombinedIdent from a rust function name.
/// C++ will use the CamelCase version of the function name.
pub fn from_rust_function(ident: Ident) -> Self {
Self {
cpp: format_ident!("{}", ident.to_string().to_case(Case::Camel)),
rust: ident,
///
/// This will consider any cxx_name or rust_name attributes
pub fn from_rust_function(attrs: &[Attribute], ident: &Ident) -> Self {
let mut combined = Self {
cpp: ident.clone(),
rust: ident.clone(),
};

// Find any cxx_name
if let Some(index) = attribute_find_path(attrs, &["cxx_name"]) {
if let Ok(name_value) = &attrs[index].meta.require_name_value() {
if let Ok(value_str) = expr_to_string(&name_value.value) {
combined.cpp = format_ident!("{value_str}");
}
}
}

// Find any rust_name
if let Some(index) = attribute_find_path(attrs, &["rust_name"]) {
if let Ok(name_value) = &attrs[index].meta.require_name_value() {
if let Ok(value_str) = expr_to_string(&name_value.value) {
combined.rust = format_ident!("{value_str}");
}
}
}

combined
}
}

#[cfg(test)]
mod tests {
use syn::{parse_quote, ForeignItemFn};

use super::*;

#[test]
fn test_from_rust_function_camel_case_conversion() {
let ident = format_ident!("test_function");
let combined = CombinedIdent::from_rust_function(ident.clone());
fn test_from_rust_function() {
let method: ForeignItemFn = parse_quote! {
fn test_function();
};
let combined = CombinedIdent::from_rust_function(&method.attrs, &method.sig.ident);
assert_eq!(combined.cpp, format_ident!("test_function"));
assert_eq!(combined.rust, format_ident!("test_function"));
}

#[test]
fn test_from_rust_function_cxx_name() {
let method: ForeignItemFn = parse_quote! {
#[cxx_name = "testFunction"]
fn test_function();
};
let combined = CombinedIdent::from_rust_function(&method.attrs, &method.sig.ident);
assert_eq!(combined.cpp, format_ident!("testFunction"));
assert_eq!(combined.rust, ident);
assert_eq!(combined.rust, format_ident!("test_function"));
}

#[test]
fn test_from_rust_function_single_word() {
let ident = format_ident!("test");
let combined = CombinedIdent::from_rust_function(ident.clone());
assert_eq!(combined.cpp, ident);
assert_eq!(combined.rust, ident);
fn test_from_rust_function_rust_name() {
let method: ForeignItemFn = parse_quote! {
#[rust_name = "test_function"]
fn testFunction();
};
let combined = CombinedIdent::from_rust_function(&method.attrs, &method.sig.ident);
assert_eq!(combined.cpp, format_ident!("testFunction"));
assert_eq!(combined.rust, format_ident!("test_function"));
}
}
19 changes: 9 additions & 10 deletions crates/cxx-qt-gen/src/generator/naming/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
//
// SPDX-License-Identifier: MIT OR Apache-2.0
use crate::{generator::naming::CombinedIdent, parser::method::ParsedMethod};
use convert_case::{Case, Casing};
use quote::format_ident;
use syn::{ForeignItemFn, Ident};
use syn::ForeignItemFn;

/// Names for parts of a method (which could be a Q_INVOKABLE)
pub struct QMethodName {
Expand All @@ -22,20 +21,19 @@ impl From<&ParsedMethod> for QMethodName {
impl From<&ForeignItemFn> for QMethodName {
fn from(method: &ForeignItemFn) -> Self {
let ident = &method.sig.ident;
Self {
name: CombinedIdent::from_rust_function(ident.clone()),
wrapper: CombinedIdent::wrapper_from_invokable(ident),
}
let name = CombinedIdent::from_rust_function(&method.attrs, ident);
let wrapper = CombinedIdent::wrapper_from_invokable(&name);

Self { name, wrapper }
}
}

impl CombinedIdent {
/// For a given ident generate the Rust and C++ wrapper names
fn wrapper_from_invokable(ident: &Ident) -> Self {
let ident = format_ident!("{ident}_wrapper");
fn wrapper_from_invokable(ident: &CombinedIdent) -> Self {
Self {
cpp: format_ident!("{}", ident.to_string().to_case(Case::Camel)),
rust: ident,
cpp: format_ident!("{ident_cpp}Wrapper", ident_cpp = ident.cpp),
rust: format_ident!("{ident_rust}_wrapper", ident_rust = ident.rust),
}
}
}
Expand All @@ -52,6 +50,7 @@ mod tests {
fn test_from_impl_method() {
let parsed = ParsedMethod {
method: parse_quote! {
#[cxx_name = "myInvokable"]
fn my_invokable(self: &MyObject);
},
qobject_ident: format_ident!("MyObject"),
Expand Down
7 changes: 3 additions & 4 deletions crates/cxx-qt-gen/src/generator/naming/signals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
use crate::generator::naming::CombinedIdent;
use crate::parser::signals::ParsedSignal;
use convert_case::{Case, Casing};
use quote::format_ident;
use syn::Ident;

Expand All @@ -26,16 +25,16 @@ impl From<&ParsedSignal> for QSignalName {
}

fn on_from_signal(ident: &Ident) -> Ident {
format_ident!("on_{}", ident.to_string().to_case(Case::Snake))
format_ident!("on_{}", ident.to_string())
}

impl CombinedIdent {
fn connect_from_signal(ident: &CombinedIdent) -> Self {
Self {
// Use signalConnect instead of onSignal here so that we don't
// create a C++ name that is similar to the QML naming scheme for signals
cpp: format_ident!("{}Connect", ident.cpp.to_string().to_case(Case::Camel)),
rust: format_ident!("connect_{}", ident.rust.to_string().to_case(Case::Snake)),
cpp: format_ident!("{}Connect", ident.cpp.to_string()),
rust: format_ident!("connect_{}", ident.rust.to_string()),
}
}
}
Expand Down
56 changes: 19 additions & 37 deletions crates/cxx-qt-gen/src/generator/rust/inherit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,52 +4,38 @@
// SPDX-License-Identifier: MIT OR Apache-2.0

use crate::{
generator::{naming::qobject::QObjectName, rust::qobject::GeneratedRustQObject},
parser::inherit::ParsedInheritedMethod,
generator::rust::qobject::GeneratedRustQObject, parser::inherit::ParsedInheritedMethod,
syntax::attribute::attribute_take_path,
};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Item, Result};

pub fn generate(
qobject_ident: &QObjectName,
methods: &[ParsedInheritedMethod],
) -> Result<GeneratedRustQObject> {
pub fn generate(methods: &[ParsedInheritedMethod]) -> Result<GeneratedRustQObject> {
let mut blocks = GeneratedRustQObject::default();
let qobject_name = &qobject_ident.cpp_class.rust;

let mut bridges = methods
.iter()
.map(|method| {
let parameters = method
.parameters
.iter()
.map(|parameter| {
let ident = &parameter.ident;
let ty = &parameter.ty;
quote! { #ident: #ty }
})
.collect::<Vec<TokenStream>>();
let ident = &method.method.sig.ident;
let cxx_name_string = &method.wrapper_ident().to_string();
let self_param = if method.mutable {
quote! { self: Pin<&mut #qobject_name> }
} else {
quote! { self: &#qobject_name }
.map(|inherit_method| {
let wrapper_ident_cpp_str = &inherit_method.wrapper_ident().to_string();

// Remove any cxx_name attribute on the original method
// As we need it to use the wrapper ident
let original_method = {
let mut original_method = inherit_method.method.clone();
attribute_take_path(&mut original_method.attrs, &["cxx_name"]);
original_method
};
let return_type = &method.method.sig.output;

let mut unsafe_block = None;
let mut unsafe_call = Some(quote! { unsafe });
if method.safe {
if inherit_method.safe {
std::mem::swap(&mut unsafe_call, &mut unsafe_block);
}
let attrs = &method.method.attrs;

syn::parse2(quote! {
#unsafe_block extern "C++" {
#(#attrs)*
#[cxx_name = #cxx_name_string]
#unsafe_call fn #ident(#self_param, #(#parameters),*) #return_type;
#[cxx_name = #wrapper_ident_cpp_str]
#original_method
}
})
})
Expand All @@ -62,18 +48,15 @@ pub fn generate(
#[cfg(test)]
mod tests {
use super::*;
use crate::{
generator::naming::qobject::tests::create_qobjectname, syntax::safety::Safety,
tests::assert_tokens_eq,
};
use crate::{syntax::safety::Safety, tests::assert_tokens_eq};
use syn::{parse_quote, ForeignItemFn};

fn generate_from_foreign(
method: ForeignItemFn,
safety: Safety,
) -> Result<GeneratedRustQObject> {
let inherited_methods = vec![ParsedInheritedMethod::parse(method, safety).unwrap()];
generate(&create_qobjectname(), &inherited_methods)
generate(&inherited_methods)
}

#[test]
Expand Down Expand Up @@ -139,11 +122,10 @@ mod tests {

assert_tokens_eq(
&generated.cxx_mod_contents[0],
// TODO: Maybe remove the trailing comma after self?
quote! {
extern "C++" {
#[cxx_name = "testCxxQtInherit"]
unsafe fn test(self: &MyObject,);
unsafe fn test(self: &MyObject);
}
},
);
Expand Down
Loading
Loading