Skip to content

Commit 9f08e5a

Browse files
Automatically generate upcasting implementations
- Any bridges with qobjects that have no custom base class will get an upcasting implemention - Adds a new include with the necessary template function for calling static_cast
1 parent 0acc26b commit 9f08e5a

File tree

20 files changed

+312
-52
lines changed

20 files changed

+312
-52
lines changed

crates/cxx-qt-gen/src/generator/cpp/qobject.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,12 @@ impl GeneratedCppQObject {
174174
class_initializers.push(initializer);
175175
}
176176

177+
// Include casting header
178+
let mut result = GeneratedCppQObjectBlocks::default();
179+
result.includes.insert("#include <cxx-qt/casting.h>".into());
180+
181+
generated.blocks.append(&mut result);
182+
177183
generated.blocks.append(&mut constructor::generate(
178184
&generated,
179185
&structured_qobject.constructors,

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

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub mod signals;
1616
pub mod threading;
1717

1818
use crate::generator::{rust::fragment::GeneratedRustFragment, structuring};
19-
use crate::parser::{parameter::ParsedFunctionParameter, Parser};
19+
use crate::parser::{parameter::ParsedFunctionParameter, qobject::ParsedQObject, Parser};
2020
use proc_macro2::{Ident, TokenStream};
2121
use quote::quote;
2222
use syn::{parse_quote, Item, ItemMod, Result};
@@ -60,6 +60,8 @@ impl GeneratedRustBlocks {
6060
let namespace = parser.cxx_qt_data.namespace.clone().unwrap_or_default();
6161
let passthrough_mod = &parser.passthrough_module;
6262

63+
fragments.extend(vec![add_qobject_import(&parser.cxx_qt_data.qobjects)]);
64+
6365
let vis = &passthrough_mod.vis;
6466
let ident = &passthrough_mod.module_ident;
6567
let docs = &passthrough_mod.docs;
@@ -95,6 +97,29 @@ impl GeneratedRustBlocks {
9597
}
9698
}
9799

100+
fn add_qobject_import(qobjects: &[ParsedQObject]) -> GeneratedRustFragment {
101+
let includes = qobjects
102+
.iter()
103+
.any(|obj| obj.has_qobject_macro && obj.base_class.is_none());
104+
if includes {
105+
GeneratedRustFragment {
106+
cxx_mod_contents: vec![parse_quote! {
107+
extern "C++" {
108+
#[doc(hidden)]
109+
#[namespace=""]
110+
type QObject = cxx_qt::qobject::QObject;
111+
}
112+
}],
113+
cxx_qt_mod_contents: vec![],
114+
}
115+
} else {
116+
GeneratedRustFragment {
117+
cxx_mod_contents: vec![],
118+
cxx_qt_mod_contents: vec![],
119+
}
120+
}
121+
}
122+
98123
/// Return the [TokenStream] of the parsed parameters for use in generation
99124
pub fn get_params_tokens(
100125
mutable: bool,
@@ -143,7 +168,7 @@ mod tests {
143168
assert!(rust.cxx_mod.content.is_none());
144169
assert_eq!(rust.cxx_mod_contents.len(), 0);
145170
assert_eq!(rust.namespace, "");
146-
assert_eq!(rust.fragments.len(), 1);
171+
assert_eq!(rust.fragments.len(), 2);
147172
}
148173

149174
#[test]
@@ -163,7 +188,7 @@ mod tests {
163188
assert!(rust.cxx_mod.content.is_none());
164189
assert_eq!(rust.cxx_mod_contents.len(), 0);
165190
assert_eq!(rust.namespace, "cxx_qt");
166-
assert_eq!(rust.fragments.len(), 1);
191+
assert_eq!(rust.fragments.len(), 2);
167192
}
168193

169194
#[test]
@@ -183,6 +208,6 @@ mod tests {
183208
assert!(rust.cxx_mod.content.is_none());
184209
assert_eq!(rust.cxx_mod_contents.len(), 0);
185210
assert_eq!(rust.namespace, "");
186-
assert_eq!(rust.fragments.len(), 1);
211+
assert_eq!(rust.fragments.len(), 2);
187212
}
188213
}

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

Lines changed: 65 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//
44
// SPDX-License-Identifier: MIT OR Apache-2.0
55
use crate::generator::structuring::StructuredQObject;
6+
use crate::naming::Name;
67
use crate::{
78
generator::{
89
naming::{namespace::NamespaceName, qobject::QObjectNames},
@@ -14,7 +15,7 @@ use crate::{
1415
},
1516
naming::TypeNames,
1617
};
17-
use quote::quote;
18+
use quote::{format_ident, quote};
1819
use syn::{parse_quote, Attribute, Ident, Result};
1920

2021
impl GeneratedRustFragment {
@@ -33,7 +34,6 @@ impl GeneratedRustFragment {
3334
&qobject_names,
3435
qobject.base_class.clone(),
3536
type_names,
36-
&qobject.cfgs,
3737
)?,
3838
generate_rust_properties(
3939
&qobject.properties,
@@ -77,18 +77,60 @@ impl GeneratedRustFragment {
7777
)?,
7878
cxxqttype::generate(&qobject_names, type_names, &qobject.cfgs)?,
7979
]);
80+
// Generate casting impl
81+
let mut blocks = GeneratedRustFragment::default();
82+
let base = structured_qobject
83+
.declaration
84+
.base_class
85+
.as_ref()
86+
.map(|name| type_names.lookup(name))
87+
.transpose()?
88+
.cloned()
89+
.unwrap_or(
90+
Name::new(format_ident!("QObject")).with_module(parse_quote! {::cxx_qt::qobject}),
91+
); // TODO! is this default module here causing the issues in the threading examples
92+
93+
let base_unqualified = base.rust_unqualified();
94+
let base_qualified = base.rust_qualified();
95+
96+
let struct_name = structured_qobject.declaration.name.rust_qualified();
97+
let struct_name_unqualified = structured_qobject.declaration.name.rust_unqualified();
98+
let (upcast_fn, upcast_fn_attrs, upcast_fn_qualified) = qobject_names
99+
.cxx_qt_ffi_method("upcastPtr")
100+
.into_cxx_parts();
101+
let fragment = GeneratedRustFragment {
102+
cxx_mod_contents: vec![parse_quote! {
103+
extern "C++" {
104+
#[doc(hidden)]
105+
#(#upcast_fn_attrs)*
106+
unsafe fn #upcast_fn(thiz: *const #struct_name_unqualified) -> *const #base_unqualified;
107+
}
108+
}],
109+
cxx_qt_mod_contents: vec![parse_quote! {
110+
impl ::cxx_qt::Upcast<#base_qualified> for #struct_name{
111+
unsafe fn upcast_ptr(this: *const Self) -> *const #base_qualified {
112+
#upcast_fn_qualified(this)
113+
}
114+
}
115+
}],
116+
};
117+
118+
generated.push(fragment);
119+
generated.push(blocks);
120+
121+
generated.push(constructor::generate(
122+
&structured_qobject.constructors,
123+
&qobject_names,
124+
&namespace_idents,
125+
type_names,
126+
)?);
80127

81128
Ok(GeneratedRustFragment::flatten(generated))
82129
}
83130
}
84131

85132
/// Generate the C++ and Rust CXX definitions for the QObject
86-
fn generate_qobject_definitions(
87-
qobject_idents: &QObjectNames,
88-
base: Option<Ident>,
89-
type_names: &TypeNames,
90-
cfgs: &[Attribute],
91-
) -> Result<GeneratedRustFragment> {
133+
fn generate_qobject_definitions(qobject_idents: &QObjectNames) -> Result<GeneratedRustFragment> {
92134
let cpp_class_name_rust = &qobject_idents.name.rust_unqualified();
93135
let cpp_class_name_cpp = &qobject_idents.name.cxx_unqualified();
94136

@@ -106,29 +148,6 @@ fn generate_qobject_definitions(
106148
}
107149
};
108150

109-
let cpp_struct_qualified = &qobject_idents.name.rust_qualified();
110-
111-
let base_upcast = if let Some(base) = base {
112-
let base_name = type_names.lookup(&base)?.rust_qualified();
113-
vec![
114-
parse_quote! {
115-
#(#cfgs)*
116-
impl cxx_qt::Upcast<#base_name> for #cpp_struct_qualified {}
117-
},
118-
// Until we can actually implement the Upcast trait properly, we just need to silence
119-
// the warning that the base class is otherwise unused.
120-
// This can be done with an unnamed import and the right attributes
121-
parse_quote! {
122-
#[allow(unused_imports)]
123-
#[allow(dead_code)]
124-
#(#cfgs)*
125-
use #base_name as _;
126-
},
127-
]
128-
} else {
129-
vec![]
130-
};
131-
132151
Ok(GeneratedRustFragment {
133152
cxx_mod_contents: vec![
134153
parse_quote! {
@@ -157,7 +176,7 @@ fn generate_qobject_definitions(
157176
}
158177
},
159178
],
160-
cxx_qt_mod_contents: base_upcast,
179+
cxx_qt_mod_contents: vec![],
161180
})
162181
}
163182

@@ -204,7 +223,7 @@ mod tests {
204223
&parser.type_names,
205224
)
206225
.unwrap();
207-
assert_eq!(rust.cxx_mod_contents.len(), 6);
226+
assert_eq!(rust.cxx_mod_contents.len(), 7);
208227
assert_tokens_eq(
209228
&rust.cxx_mod_contents[0],
210229
quote! {
@@ -239,6 +258,17 @@ mod tests {
239258
);
240259
assert_tokens_eq(
241260
&rust.cxx_mod_contents[3],
261+
quote! {
262+
extern "C++" {
263+
#[doc(hidden)]
264+
#[cxx_name = "upcastPtr"]
265+
#[namespace = "rust::cxxqt1"]
266+
unsafe fn cxx_qt_ffi_MyObject_upcastPtr(thiz: *const MyObject) -> *const QObject;
267+
}
268+
},
269+
);
270+
assert_tokens_eq(
271+
&rust.cxx_mod_contents[4],
242272
quote! {
243273
extern "Rust" {
244274
#[cxx_name = "createRs"]
@@ -248,7 +278,7 @@ mod tests {
248278
},
249279
);
250280
assert_tokens_eq(
251-
&rust.cxx_mod_contents[4],
281+
&rust.cxx_mod_contents[5],
252282
quote! {
253283
unsafe extern "C++" {
254284
#[doc(hidden)]
@@ -259,7 +289,7 @@ mod tests {
259289
},
260290
);
261291
assert_tokens_eq(
262-
&rust.cxx_mod_contents[5],
292+
&rust.cxx_mod_contents[6],
263293
quote! {
264294
unsafe extern "C++" {
265295
#[doc(hidden)]

crates/cxx-qt-gen/src/writer/cpp/header.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ mod tests {
256256
let expected = indoc! {r#"
257257
#pragma once
258258
259+
#include <cxx-qt/casting.h>
259260
#include <cxx-qt/type.h>
260261
261262
class MyObject;

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include <cxx-qt/casting.h>
34
#include <cxx-qt/type.h>
45

56
class MyObject;

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ mod inheritance {
6161
#[doc = " Inherited fetchMore from the base class"]
6262
unsafe fn fetch_more(self: Pin<&mut MyObject>, index: &QModelIndex);
6363
}
64+
extern "C++" {
65+
#[doc(hidden)]
66+
#[cxx_name = "upcastPtr"]
67+
#[namespace = "rust::cxxqt1"]
68+
unsafe fn cxx_qt_ffi_MyObject_upcastPtr(thiz: *const MyObject)
69+
-> *const QAbstractItemModel;
70+
}
6471
extern "Rust" {
6572
#[cxx_name = "createRs"]
6673
#[namespace = "cxx_qt_MyObject"]
@@ -79,10 +86,11 @@ mod inheritance {
7986
fn cxx_qt_ffi_MyObject_unsafeRustMut(outer: Pin<&mut MyObject>) -> Pin<&mut MyObjectRust>;
8087
}
8188
}
82-
impl cxx_qt::Upcast<inheritance::QAbstractItemModel> for inheritance::MyObject {}
83-
#[allow(unused_imports)]
84-
#[allow(dead_code)]
85-
use inheritance::QAbstractItemModel as _;
89+
impl ::cxx_qt::Upcast<inheritance::QAbstractItemModel> for inheritance::MyObject {
90+
unsafe fn upcast_ptr(this: *const Self) -> *const inheritance::QAbstractItemModel {
91+
inheritance::cxx_qt_ffi_MyObject_upcastPtr(this)
92+
}
93+
}
8694
#[doc(hidden)]
8795
#[allow(clippy::unnecessary_box_returns)]
8896
pub fn create_rs_MyObjectRust() -> std::boxed::Box<MyObjectRust> {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include <cxx-qt/casting.h>
34
#include <cxx-qt/threading.h>
45
#include <cxx-qt/type.h>
56

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,12 @@ mod ffi {
146146
#[namespace = "cxx_qt::my_object::cxx_qt_MyObject"]
147147
type MyObjectCxxQtThreadQueuedFn;
148148
}
149+
extern "C++" {
150+
#[doc(hidden)]
151+
#[cxx_name = "upcastPtr"]
152+
#[namespace = "rust::cxxqt1"]
153+
unsafe fn cxx_qt_ffi_MyObject_upcastPtr(thiz: *const MyObject) -> *const QObject;
154+
}
149155
#[namespace = "cxx_qt::my_object::cxx_qt_MyObject"]
150156
#[cxx_name = "CxxQtConstructorArguments0"]
151157
#[doc(hidden)]
@@ -247,6 +253,11 @@ mod ffi {
247253
#[namespace = "rust::cxxqt1"]
248254
fn cxx_qt_ffi_MyObject_unsafeRustMut(outer: Pin<&mut MyObject>) -> Pin<&mut MyObjectRust>;
249255
}
256+
extern "C++" {
257+
#[doc(hidden)]
258+
#[namespace = ""]
259+
type QObject = cxx_qt::qobject::QObject;
260+
}
250261
}
251262
impl cxx_qt::Threading for ffi::MyObject {
252263
type BoxedQueuedFn = MyObjectCxxQtThreadQueuedFn;
@@ -300,6 +311,11 @@ impl cxx_qt::Threading for ffi::MyObject {
300311
pub struct MyObjectCxxQtThreadQueuedFn {
301312
inner: std::boxed::Box<dyn FnOnce(core::pin::Pin<&mut ffi::MyObject>) + Send>,
302313
}
314+
impl ::cxx_qt::Upcast<::cxx_qt::qobject::QObject> for ffi::MyObject {
315+
unsafe fn upcast_ptr(this: *const Self) -> *const ::cxx_qt::qobject::QObject {
316+
ffi::cxx_qt_ffi_MyObject_upcastPtr(this)
317+
}
318+
}
303319
#[doc(hidden)]
304320
pub fn route_arguments_MyObject_0<'a>(
305321
arg0: i32,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include <cxx-qt/casting.h>
34
#include <cxx-qt/signalhandler.h>
45
#include <cxx-qt/type.h>
56

0 commit comments

Comments
 (0)