Skip to content

Commit 15fa262

Browse files
Add attribute which generates wrappers for a function
1 parent 723ba1e commit 15fa262

File tree

6 files changed

+85
-24
lines changed

6 files changed

+85
-24
lines changed

crates/cxx-qt-gen/src/generator/rust/method.rs

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//
44
// SPDX-License-Identifier: MIT OR Apache-2.0
55

6-
use crate::generator::rust::get_params_tokens;
6+
use crate::generator::rust::{get_call_params_tokens, get_params_tokens};
77
use crate::{
88
generator::{naming::qobject::QObjectNames, rust::fragment::GeneratedRustFragment},
99
parser::method::ParsedMethod,
@@ -31,6 +31,8 @@ pub fn generate_rust_methods(
3131
cpp_class_name_rust,
3232
);
3333

34+
let call_parameters = get_call_params_tokens(&invokable.parameters);
35+
3436
let return_type = &invokable.method.sig.output;
3537

3638
let cfgs = &invokable.cfgs;
@@ -54,24 +56,38 @@ pub fn generate_rust_methods(
5456
Some(quote! { unsafe })
5557
};
5658

57-
GeneratedRustFragment::from_cxx_item(parse_quote_spanned! {
58-
invokable.method.span() =>
59-
// Note: extern "Rust" block does not need to be unsafe
60-
#block_safety extern #block_type {
61-
// Note that we are exposing a Rust method on the C++ type to C++
62-
//
63-
// CXX ends up generating the source, then we generate the matching header.
64-
#[cxx_name = #invokable_ident_cpp]
65-
// Needed for QObjects to have a namespace on their type or extern block
66-
//
67-
// A Namespace from cxx_qt::bridge would be automatically applied to all children
68-
// but to apply it to only certain types, it is needed here too
69-
#cxx_namespace
70-
#(#cfgs)*
71-
#[doc(hidden)]
72-
#unsafe_call fn #invokable_ident_rust(#parameter_signatures) #return_type;
73-
}
74-
})
59+
let wrapper_fn = if invokable.wrap {
60+
vec![parse_quote_spanned! {
61+
invokable.method.span() =>
62+
#unsafe_call fn #invokable_ident_rust(#parameter_signatures) #return_type {
63+
self.rust().#invokable_ident_rust(#call_parameters)
64+
}
65+
}]
66+
} else {
67+
vec![]
68+
};
69+
70+
GeneratedRustFragment {
71+
cxx_mod_contents: vec![parse_quote_spanned! {
72+
invokable.method.span() =>
73+
// Note: extern "Rust" block does not need to be unsafe
74+
#block_safety extern #block_type {
75+
// Note that we are exposing a Rust method on the C++ type to C++
76+
//
77+
// CXX ends up generating the source, then we generate the matching header.
78+
#[cxx_name = #invokable_ident_cpp]
79+
// Needed for QObjects to have a namespace on their type or extern block
80+
//
81+
// A Namespace from cxx_qt::bridge would be automatically applied to all children
82+
// but to apply it to only certain types, it is needed here too
83+
#cxx_namespace
84+
#(#cfgs)*
85+
#[doc(hidden)]
86+
#unsafe_call fn #invokable_ident_rust(#parameter_signatures) #return_type;
87+
}
88+
}],
89+
cxx_qt_mod_contents: wrapper_fn,
90+
}
7591
})
7692
.collect::<Vec<_>>();
7793

@@ -98,10 +114,12 @@ mod tests {
98114
};
99115
let method3: ForeignItemFn = parse_quote! {
100116
#[cxx_name = "opaqueInvokable"]
117+
#[auto_wrap]
101118
fn opaque_invokable(self: Pin<&mut MyObject>, param: &QColor) -> UniquePtr<QColor>;
102119
};
103120
let method4: ForeignItemFn = parse_quote! {
104121
#[cxx_name = "unsafeInvokable"]
122+
#[auto_wrap]
105123
unsafe fn unsafe_invokable(self: &MyObject, param: *mut T) -> *mut T;
106124
};
107125
let invokables = vec![
@@ -116,7 +134,7 @@ mod tests {
116134
generate_rust_methods(&invokables.iter().collect::<Vec<_>>(), &qobject_names).unwrap();
117135

118136
assert_eq!(generated.cxx_mod_contents.len(), 4);
119-
assert_eq!(generated.cxx_qt_mod_contents.len(), 0);
137+
assert_eq!(generated.cxx_qt_mod_contents.len(), 2);
120138

121139
// void_invokable
122140
assert_tokens_eq(
@@ -154,6 +172,15 @@ mod tests {
154172
},
155173
);
156174

175+
assert_tokens_eq(
176+
&generated.cxx_qt_mod_contents[0],
177+
quote! {
178+
fn opaque_invokable(self: Pin<&mut MyObject>, param: &QColor) -> UniquePtr<QColor> {
179+
self.rust().opaque_invokable(param)
180+
}
181+
},
182+
);
183+
157184
// unsafe_invokable
158185
assert_tokens_eq(
159186
&generated.cxx_mod_contents[3],
@@ -165,5 +192,14 @@ mod tests {
165192
}
166193
},
167194
);
195+
196+
assert_tokens_eq(
197+
&generated.cxx_qt_mod_contents[1],
198+
quote! {
199+
unsafe fn unsafe_invokable(self:&MyObject, param: *mut T) -> *mut T {
200+
self.rust().unsafe_invokable(param)
201+
}
202+
},
203+
);
168204
}
169205
}

crates/cxx-qt-gen/src/generator/rust/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,22 @@ pub fn get_params_tokens(
146146
}
147147
}
148148

149+
/// Return the [TokenStream] of the parsed parameters, which would be used to call the fn, for use in generation
150+
pub fn get_call_params_tokens(parameters: &[ParsedFunctionParameter]) -> TokenStream {
151+
if parameters.is_empty() {
152+
quote! {}
153+
} else {
154+
let parameters = parameters
155+
.iter()
156+
.map(|parameter| {
157+
let ident = &parameter.ident;
158+
quote! { #ident }
159+
})
160+
.collect::<Vec<TokenStream>>();
161+
quote! { #(#parameters),* }
162+
}
163+
}
164+
149165
#[cfg(test)]
150166
mod tests {
151167
use super::*;

crates/cxx-qt-gen/src/parser/method.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ pub struct ParsedMethod {
5757
pub is_qinvokable: bool,
5858
/// Whether the method is a pure virtual method
5959
pub is_pure: bool,
60+
/// Whether to auto generate a wrapper for this method outside the bridge
61+
pub wrap: bool,
6062
// No docs field since the docs should be on the method implementation outside the bridge
6163
// This means any docs on the bridge declaration would be ignored
6264
/// Cfgs for the method
@@ -66,14 +68,15 @@ pub struct ParsedMethod {
6668
}
6769

6870
impl ParsedMethod {
69-
const ALLOWED_ATTRS: [&'static str; 9] = [
71+
const ALLOWED_ATTRS: [&'static str; 10] = [
7072
"cxx_name",
7173
"rust_name",
7274
"qinvokable",
7375
"cxx_final",
7476
"cxx_override",
7577
"cxx_virtual",
7678
"cxx_pure",
79+
"auto_wrap",
7780
"doc",
7881
"cfg",
7982
];
@@ -125,13 +128,15 @@ impl ParsedMethod {
125128
// Determine if the method is invokable
126129
let is_qinvokable = attrs.contains_key("qinvokable");
127130
let is_pure = attrs.contains_key("cxx_pure");
131+
let wrap = attrs.contains_key("auto_wrap");
128132
let specifiers = ParsedQInvokableSpecifiers::from_attrs(attrs);
129133

130134
Ok(Self {
131135
method_fields: fields,
132136
specifiers,
133137
is_qinvokable,
134138
is_pure,
139+
wrap,
135140
cfgs,
136141
unsafe_block,
137142
})

crates/cxx-qt-gen/test_inputs/passthrough_and_naming.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ pub mod ffi {
144144
fn invokable_name(self: Pin<&mut SecondObject>);
145145

146146
#[cxx_name = "myRenamedFunction"]
147-
fn my_function(self: &SecondObject);
147+
#[auto_wrap]
148+
fn my_function(self: &SecondObject, param: i32);
148149
}
149150

150151
extern "RustQt" {

crates/cxx-qt-gen/test_outputs/passthrough_and_naming.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ class SecondObject
165165
Q_SLOT void setPropertyName(::std::int32_t value) noexcept;
166166
Q_SIGNAL void propertyNameChanged();
167167
Q_INVOKABLE void invokableName() noexcept;
168-
void myRenamedFunction() const noexcept;
168+
void myRenamedFunction(::std::int32_t param) const noexcept;
169169
Q_SIGNAL void ready();
170170
explicit SecondObject(QObject* parent = nullptr);
171171
};

crates/cxx-qt-gen/test_outputs/passthrough_and_naming.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ pub mod ffi {
263263
#[cxx_name = "myRenamedFunction"]
264264
#[namespace = "second_object"]
265265
#[doc(hidden)]
266-
unsafe fn my_function(self: &SecondObject);
266+
unsafe fn my_function(self: &SecondObject, param: i32);
267267
}
268268
unsafe extern "C++" {
269269
#[cxx_name = "ready"]
@@ -780,6 +780,9 @@ cxx_qt::static_assertions::assert_eq_size!(
780780
cxx_qt::signalhandler::CxxQtSignalHandler<SecondObjectCxxQtSignalClosurepropertyNameChanged>,
781781
[usize; 2]
782782
);
783+
unsafe fn my_function(self: &SecondObject, param: i32) {
784+
self.rust().my_function(param)
785+
}
783786
impl ffi::SecondObject {
784787
#[doc = "Connect the given function pointer to the signal "]
785788
#[doc = "ready"]

0 commit comments

Comments
 (0)