diff --git a/CHANGELOG.md b/CHANGELOG.md index bc1155421..595440a63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - CXX-Qt-build: Multi-crate projects are now possible with Cargo and CMake (see `examples/qml_multi_crates`) - CXX-Qt-build: Allow forcing initialization of crates/QML modules (`cxx_qt::init_crate!`/`cxx_qt::init_qml_module!`) - Add pure virtual function specified through the `#[cxx_pure]` attribute +- Add wrappers for up and down casting, for all types which inherit from QObject, available for &T, &mut T and Pin<&mut T> ### Fixed diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 7475bdb69..13c6eba23 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -21,6 +21,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 - [Types](./concepts/types.md) - [Nested Objects](./concepts/nested_objects.md) - [Inheritance & Overriding](./concepts/inheritance.md) + - [Casting](./concepts/casting.md) - [Reference: the bridge module](./bridge/index.md) - [`extern "RustQt"`](./bridge/extern_rustqt.md) - [`extern "C++Qt"`](./bridge/extern_cppqt.md) diff --git a/book/src/bridge/extern_rustqt.md b/book/src/bridge/extern_rustqt.md index 2af68a8ef..3a4126f9b 100644 --- a/book/src/bridge/extern_rustqt.md +++ b/book/src/bridge/extern_rustqt.md @@ -92,7 +92,7 @@ Use the CXX `include!` macro to include the appropriate C++ header for the base {{#include ../../../examples/qml_features/rust/src/custom_base_class.rs:book_base_include}} ``` -For more information on inheritance and how to override methods see the [Inheritance & Overriding](../concepts/inheritance.md) page. +For more information on inheritance and how to override methods see the [Inheritance & Overriding](../concepts/inheritance.md) page and the [Casting](../concepts/casting.md) page. [Full Example](https://github.com/KDAB/cxx-qt/blob/main/examples/qml_features/rust/src/custom_base_class.rs) diff --git a/book/src/bridge/traits.md b/book/src/bridge/traits.md index c0a8b6980..75c7d291b 100644 --- a/book/src/bridge/traits.md +++ b/book/src/bridge/traits.md @@ -26,3 +26,8 @@ For further documentation, refer to the documentation of the individual traits: - [Constructor](https://docs.rs/cxx-qt/latest/cxx_qt/trait.Constructor.html) - custom constructor - [Initialize](https://docs.rs/cxx-qt/latest/cxx_qt/trait.Initialize.html) - execute Rust code when the object is constructed - [Threading](https://docs.rs/cxx-qt/latest/cxx_qt/trait.Threading.html) - marker trait whether CXX-Qt threading should be enabled + +> ⚠️ These traits should only be implemented if you are sure you need to, they are automatically implemented for RustQt types. + +- [Upcast](https://docs.rs/cxx-qt/latest/cxx_qt/trait.Upcast.html) - Allows a type to access its parent class if there is one +- [Downcast](https://docs.rs/cxx-qt/latest/cxx_qt/trait.Downcast.html) - Allows a type to access its child class if there is one diff --git a/book/src/concepts/casting.md b/book/src/concepts/casting.md new file mode 100644 index 000000000..8f70d3dd2 --- /dev/null +++ b/book/src/concepts/casting.md @@ -0,0 +1,42 @@ + + +# Casting + +With the [base](../bridge/attributes.md) attribute, it is possible to inherit from another type. +In order to access this parent class, we provide an API to cast up or down. +Currently, this is only supported for objects in `extern "RustQt"` blocks, which have either a `#[qobject]` attribute, +or a `#[base = T]` attribute. see [here](../bridge/attributes.md) for more details on these attributes. + +## Accessing the base class + +To access the methods of a base class in Rust, use the `Upcast` trait like so `use cxx_qt::Upcast;`. +Objects with base classes can then be accessed with the following methods + +| Self Type | Method | +|------------------|----------------| +| `&self` | `upcast()` | +| `&mut self` | `upcast_mut()` | +| `Pin<&mut self>` | `upcast_pin()` | + +This will then return a reference to the base in the same format as the self type, e.g. `upcast()` returns `&Base`, etc... + +## Accessing the child class + +This also works in the opposite direction, allowing access to the child a base class was obtained from. +To do this, use the `Downcast` trait like so `use cxx_qt::Downcast;`. +The child can then be accessed in the same manner, with the following methods + +| Self Type | Method | +|------------------|------------------| +| `&self` | `downcast()` | +| `&mut self` | `downcast_mut()` | +| `Pin<&mut self>` | `downcast_pin()` | + +These will return an `Option`, as it is possible that downcasting will fail, +if the type is not actually of the given subclass, +and these also return in the same format as the self type, e.g. `downcast()` returns `Option<&Sub>`, etc... diff --git a/crates/cxx-qt-gen/src/generator/cpp/qobject.rs b/crates/cxx-qt-gen/src/generator/cpp/qobject.rs index 47183a1ee..323f258ff 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/qobject.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/qobject.rs @@ -174,6 +174,12 @@ impl GeneratedCppQObject { class_initializers.push(initializer); } + // Include casting header + let mut result = GeneratedCppQObjectBlocks::default(); + result.includes.insert("#include ".into()); + + generated.blocks.append(&mut result); + generated.blocks.append(&mut constructor::generate( &generated, &structured_qobject.constructors, diff --git a/crates/cxx-qt-gen/src/generator/rust/mod.rs b/crates/cxx-qt-gen/src/generator/rust/mod.rs index ba5985b58..6992e1864 100644 --- a/crates/cxx-qt-gen/src/generator/rust/mod.rs +++ b/crates/cxx-qt-gen/src/generator/rust/mod.rs @@ -16,7 +16,7 @@ pub mod signals; pub mod threading; use crate::generator::{rust::fragment::GeneratedRustFragment, structuring}; -use crate::parser::{parameter::ParsedFunctionParameter, Parser}; +use crate::parser::{parameter::ParsedFunctionParameter, qobject::ParsedQObject, Parser}; use proc_macro2::{Ident, TokenStream}; use quote::quote; use syn::{parse_quote, Item, ItemMod, Result}; @@ -60,6 +60,8 @@ impl GeneratedRustBlocks { let namespace = parser.cxx_qt_data.namespace.clone().unwrap_or_default(); let passthrough_mod = &parser.passthrough_module; + fragments.extend(vec![add_qobject_import(&parser.cxx_qt_data.qobjects)]); + let vis = &passthrough_mod.vis; let ident = &passthrough_mod.module_ident; let docs = &passthrough_mod.docs; @@ -95,6 +97,29 @@ impl GeneratedRustBlocks { } } +fn add_qobject_import(qobjects: &[ParsedQObject]) -> GeneratedRustFragment { + let includes = qobjects + .iter() + .any(|obj| obj.has_qobject_macro && obj.base_class.is_none()); + if includes { + GeneratedRustFragment { + cxx_mod_contents: vec![parse_quote! { + extern "C++" { + #[doc(hidden)] + #[namespace=""] + type QObject = cxx_qt::QObject; + } + }], + cxx_qt_mod_contents: vec![], + } + } else { + GeneratedRustFragment { + cxx_mod_contents: vec![], + cxx_qt_mod_contents: vec![], + } + } +} + /// Return the [TokenStream] of the parsed parameters for use in generation pub fn get_params_tokens( mutable: bool, @@ -143,7 +168,7 @@ mod tests { assert!(rust.cxx_mod.content.is_none()); assert_eq!(rust.cxx_mod_contents.len(), 0); assert_eq!(rust.namespace, ""); - assert_eq!(rust.fragments.len(), 1); + assert_eq!(rust.fragments.len(), 2); } #[test] @@ -163,7 +188,7 @@ mod tests { assert!(rust.cxx_mod.content.is_none()); assert_eq!(rust.cxx_mod_contents.len(), 0); assert_eq!(rust.namespace, "cxx_qt"); - assert_eq!(rust.fragments.len(), 1); + assert_eq!(rust.fragments.len(), 2); } #[test] @@ -183,6 +208,6 @@ mod tests { assert!(rust.cxx_mod.content.is_none()); assert_eq!(rust.cxx_mod_contents.len(), 0); assert_eq!(rust.namespace, ""); - assert_eq!(rust.fragments.len(), 1); + assert_eq!(rust.fragments.len(), 2); } } diff --git a/crates/cxx-qt-gen/src/generator/rust/qobject.rs b/crates/cxx-qt-gen/src/generator/rust/qobject.rs index 46c8415f3..3569d9e48 100644 --- a/crates/cxx-qt-gen/src/generator/rust/qobject.rs +++ b/crates/cxx-qt-gen/src/generator/rust/qobject.rs @@ -3,6 +3,7 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::generator::structuring::StructuredQObject; +use crate::naming::Name; use crate::{ generator::{ naming::{namespace::NamespaceName, qobject::QObjectNames}, @@ -14,8 +15,8 @@ use crate::{ }, naming::TypeNames, }; -use quote::quote; -use syn::{parse_quote, Attribute, Ident, Result}; +use quote::{format_ident, quote}; +use syn::{parse_quote, Attribute, Result}; impl GeneratedRustFragment { // Might need to be refactored to use a StructuredQObject instead (confirm with Leon) @@ -29,12 +30,7 @@ impl GeneratedRustFragment { let namespace_idents = NamespaceName::from(qobject); let mut generated = vec![ - generate_qobject_definitions( - &qobject_names, - qobject.base_class.clone(), - type_names, - &qobject.cfgs, - )?, + generate_qobject_definitions(&qobject_names, &qobject.cfgs)?, generate_rust_properties( &qobject.properties, &qobject_names, @@ -67,6 +63,53 @@ impl GeneratedRustFragment { )?); } + // Generate casting impl + let base = structured_qobject + .declaration + .base_class + .as_ref() + .map(|name| type_names.lookup(name)) + .transpose()? + .cloned() + .unwrap_or(Name::new(format_ident!("QObject")).with_module(parse_quote! {::cxx_qt})); + + let base_unqualified = base.rust_unqualified(); + let base_qualified = base.rust_qualified(); + + let struct_name = structured_qobject.declaration.name.rust_qualified(); + let struct_name_unqualified = structured_qobject.declaration.name.rust_unqualified(); + let (upcast_fn, upcast_fn_attrs, upcast_fn_qualified) = qobject_names + .cxx_qt_ffi_method("upcastPtr") + .into_cxx_parts(); + let (downcast_fn, downcast_fn_attrs, downcast_fn_qualified) = qobject_names + .cxx_qt_ffi_method("downcastPtr") + .into_cxx_parts(); + + generated.push(GeneratedRustFragment { + cxx_mod_contents: vec![parse_quote! { + extern "C++" { + #[doc(hidden)] + #(#upcast_fn_attrs)* + unsafe fn #upcast_fn(thiz: *const #struct_name_unqualified) -> *const #base_unqualified; + + #[doc(hidden)] + #(#downcast_fn_attrs)* + unsafe fn #downcast_fn(base: *const #base_unqualified) -> *const #struct_name_unqualified; + } + }], + cxx_qt_mod_contents: vec![parse_quote! { + impl ::cxx_qt::Upcast<#base_qualified> for #struct_name{ + unsafe fn upcast_ptr(this: *const Self) -> *const #base_qualified { + #upcast_fn_qualified(this) + } + + unsafe fn from_base_ptr(base: *const #base_qualified) -> *const Self { + #downcast_fn_qualified(base) + } + } + }], + }); + generated.extend(vec![ constructor::generate( &structured_qobject.constructors, @@ -85,8 +128,6 @@ impl GeneratedRustFragment { /// Generate the C++ and Rust CXX definitions for the QObject fn generate_qobject_definitions( qobject_idents: &QObjectNames, - base: Option, - type_names: &TypeNames, cfgs: &[Attribute], ) -> Result { let cpp_class_name_rust = &qobject_idents.name.rust_unqualified(); @@ -106,29 +147,6 @@ fn generate_qobject_definitions( } }; - let cpp_struct_qualified = &qobject_idents.name.rust_qualified(); - - let base_upcast = if let Some(base) = base { - let base_name = type_names.lookup(&base)?.rust_qualified(); - vec![ - parse_quote! { - #(#cfgs)* - impl cxx_qt::Upcast<#base_name> for #cpp_struct_qualified {} - }, - // Until we can actually implement the Upcast trait properly, we just need to silence - // the warning that the base class is otherwise unused. - // This can be done with an unnamed import and the right attributes - parse_quote! { - #[allow(unused_imports)] - #[allow(dead_code)] - #(#cfgs)* - use #base_name as _; - }, - ] - } else { - vec![] - }; - Ok(GeneratedRustFragment { cxx_mod_contents: vec![ parse_quote! { @@ -157,7 +175,7 @@ fn generate_qobject_definitions( } }, ], - cxx_qt_mod_contents: base_upcast, + cxx_qt_mod_contents: vec![], }) } @@ -204,7 +222,7 @@ mod tests { &parser.type_names, ) .unwrap(); - assert_eq!(rust.cxx_mod_contents.len(), 6); + assert_eq!(rust.cxx_mod_contents.len(), 7); assert_tokens_eq( &rust.cxx_mod_contents[0], quote! { @@ -239,6 +257,22 @@ mod tests { ); assert_tokens_eq( &rust.cxx_mod_contents[3], + quote! { + extern "C++" { + #[doc(hidden)] + #[cxx_name = "upcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_MyObject_upcastPtr(thiz: *const MyObject) -> *const QObject; + + #[doc(hidden)] + #[cxx_name = "downcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_MyObject_downcastPtr(base: *const QObject) -> *const MyObject; + } + }, + ); + assert_tokens_eq( + &rust.cxx_mod_contents[4], quote! { extern "Rust" { #[cxx_name = "createRs"] @@ -248,7 +282,7 @@ mod tests { }, ); assert_tokens_eq( - &rust.cxx_mod_contents[4], + &rust.cxx_mod_contents[5], quote! { unsafe extern "C++" { #[doc(hidden)] @@ -259,7 +293,7 @@ mod tests { }, ); assert_tokens_eq( - &rust.cxx_mod_contents[5], + &rust.cxx_mod_contents[6], quote! { unsafe extern "C++" { #[doc(hidden)] diff --git a/crates/cxx-qt-gen/src/writer/cpp/header.rs b/crates/cxx-qt-gen/src/writer/cpp/header.rs index d05559864..645ff16ae 100644 --- a/crates/cxx-qt-gen/src/writer/cpp/header.rs +++ b/crates/cxx-qt-gen/src/writer/cpp/header.rs @@ -256,6 +256,7 @@ mod tests { let expected = indoc! {r#" #pragma once +#include #include class MyObject; diff --git a/crates/cxx-qt-gen/test_outputs/cfgs.h b/crates/cxx-qt-gen/test_outputs/cfgs.h index 440bccaba..67e943c6e 100644 --- a/crates/cxx-qt-gen/test_outputs/cfgs.h +++ b/crates/cxx-qt-gen/test_outputs/cfgs.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include diff --git a/crates/cxx-qt-gen/test_outputs/cfgs.rs b/crates/cxx-qt-gen/test_outputs/cfgs.rs index 668c8b5f4..a2c7a5f2a 100644 --- a/crates/cxx-qt-gen/test_outputs/cfgs.rs +++ b/crates/cxx-qt-gen/test_outputs/cfgs.rs @@ -158,6 +158,20 @@ mod ffi { self_value: Pin<&mut QObjectEnabled>, ); } + extern "C++" { + #[doc(hidden)] + #[cxx_name = "upcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_QObjectEnabled_upcastPtr( + thiz: *const QObjectEnabled, + ) -> *const QObject; + #[doc(hidden)] + #[cxx_name = "downcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_QObjectEnabled_downcastPtr( + base: *const QObject, + ) -> *const QObjectEnabled; + } extern "Rust" { #[cxx_name = "createRs"] #[namespace = "cxx_qt_QObjectEnabled"] @@ -284,6 +298,20 @@ mod ffi { self_value: Pin<&mut QObjectDisabled>, ); } + extern "C++" { + #[doc(hidden)] + #[cxx_name = "upcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_QObjectDisabled_upcastPtr( + thiz: *const QObjectDisabled, + ) -> *const QObject; + #[doc(hidden)] + #[cxx_name = "downcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_QObjectDisabled_downcastPtr( + base: *const QObject, + ) -> *const QObjectDisabled; + } extern "Rust" { #[cxx_name = "createRs"] #[namespace = "cxx_qt_QObjectDisabled"] @@ -450,6 +478,11 @@ mod ffi { self_value: Pin<&mut QObjectExternDisabled>, ); } + extern "C++" { + #[doc(hidden)] + #[namespace = ""] + type QObject = cxx_qt::QObject; + } } #[cfg(not(enabled))] impl ffi::QObjectEnabled { @@ -599,6 +632,14 @@ cxx_qt::static_assertions::assert_eq_size!( cxx_qt::signalhandler::CxxQtSignalHandler, [usize; 2] ); +impl ::cxx_qt::Upcast<::cxx_qt::QObject> for ffi::QObjectEnabled { + unsafe fn upcast_ptr(this: *const Self) -> *const ::cxx_qt::QObject { + ffi::cxx_qt_ffi_QObjectEnabled_upcastPtr(this) + } + unsafe fn from_base_ptr(base: *const ::cxx_qt::QObject) -> *const Self { + ffi::cxx_qt_ffi_QObjectEnabled_downcastPtr(base) + } +} #[doc(hidden)] #[allow(clippy::unnecessary_box_returns)] #[cfg(enabled)] @@ -770,6 +811,14 @@ cxx_qt::static_assertions::assert_eq_size!( cxx_qt::signalhandler::CxxQtSignalHandler, [usize; 2] ); +impl ::cxx_qt::Upcast<::cxx_qt::QObject> for ffi::QObjectDisabled { + unsafe fn upcast_ptr(this: *const Self) -> *const ::cxx_qt::QObject { + ffi::cxx_qt_ffi_QObjectDisabled_upcastPtr(this) + } + unsafe fn from_base_ptr(base: *const ::cxx_qt::QObject) -> *const Self { + ffi::cxx_qt_ffi_QObjectDisabled_downcastPtr(base) + } +} #[doc(hidden)] #[allow(clippy::unnecessary_box_returns)] #[cfg(not(enabled))] diff --git a/crates/cxx-qt-gen/test_outputs/inheritance.h b/crates/cxx-qt-gen/test_outputs/inheritance.h index ae42cf806..6e7700a3f 100644 --- a/crates/cxx-qt-gen/test_outputs/inheritance.h +++ b/crates/cxx-qt-gen/test_outputs/inheritance.h @@ -1,5 +1,6 @@ #pragma once +#include #include class MyObject; diff --git a/crates/cxx-qt-gen/test_outputs/inheritance.rs b/crates/cxx-qt-gen/test_outputs/inheritance.rs index e4574658e..5a5aba3f9 100644 --- a/crates/cxx-qt-gen/test_outputs/inheritance.rs +++ b/crates/cxx-qt-gen/test_outputs/inheritance.rs @@ -61,6 +61,19 @@ mod inheritance { #[doc = " Inherited fetchMore from the base class"] unsafe fn fetch_more(self: Pin<&mut MyObject>, index: &QModelIndex); } + extern "C++" { + #[doc(hidden)] + #[cxx_name = "upcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_MyObject_upcastPtr(thiz: *const MyObject) + -> *const QAbstractItemModel; + #[doc(hidden)] + #[cxx_name = "downcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_MyObject_downcastPtr( + base: *const QAbstractItemModel, + ) -> *const MyObject; + } extern "Rust" { #[cxx_name = "createRs"] #[namespace = "cxx_qt_MyObject"] @@ -79,10 +92,14 @@ mod inheritance { fn cxx_qt_ffi_MyObject_unsafeRustMut(outer: Pin<&mut MyObject>) -> Pin<&mut MyObjectRust>; } } -impl cxx_qt::Upcast for inheritance::MyObject {} -#[allow(unused_imports)] -#[allow(dead_code)] -use inheritance::QAbstractItemModel as _; +impl ::cxx_qt::Upcast for inheritance::MyObject { + unsafe fn upcast_ptr(this: *const Self) -> *const inheritance::QAbstractItemModel { + inheritance::cxx_qt_ffi_MyObject_upcastPtr(this) + } + unsafe fn from_base_ptr(base: *const inheritance::QAbstractItemModel) -> *const Self { + inheritance::cxx_qt_ffi_MyObject_downcastPtr(base) + } +} #[doc(hidden)] #[allow(clippy::unnecessary_box_returns)] pub fn create_rs_MyObjectRust() -> std::boxed::Box { diff --git a/crates/cxx-qt-gen/test_outputs/invokables.h b/crates/cxx-qt-gen/test_outputs/invokables.h index cc138d7df..ad7cd7e04 100644 --- a/crates/cxx-qt-gen/test_outputs/invokables.h +++ b/crates/cxx-qt-gen/test_outputs/invokables.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include diff --git a/crates/cxx-qt-gen/test_outputs/invokables.rs b/crates/cxx-qt-gen/test_outputs/invokables.rs index 1ac7a1ebc..767aef0e9 100644 --- a/crates/cxx-qt-gen/test_outputs/invokables.rs +++ b/crates/cxx-qt-gen/test_outputs/invokables.rs @@ -157,6 +157,16 @@ mod ffi { #[namespace = "cxx_qt::my_object::cxx_qt_MyObject"] type MyObjectCxxQtThreadQueuedFn; } + extern "C++" { + #[doc(hidden)] + #[cxx_name = "upcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_MyObject_upcastPtr(thiz: *const MyObject) -> *const QObject; + #[doc(hidden)] + #[cxx_name = "downcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_MyObject_downcastPtr(base: *const QObject) -> *const MyObject; + } #[namespace = "cxx_qt::my_object::cxx_qt_MyObject"] #[cxx_name = "CxxQtConstructorArguments0"] #[doc(hidden)] @@ -258,6 +268,11 @@ mod ffi { #[namespace = "rust::cxxqt1"] fn cxx_qt_ffi_MyObject_unsafeRustMut(outer: Pin<&mut MyObject>) -> Pin<&mut MyObjectRust>; } + extern "C++" { + #[doc(hidden)] + #[namespace = ""] + type QObject = cxx_qt::QObject; + } } impl cxx_qt::Threading for ffi::MyObject { type BoxedQueuedFn = MyObjectCxxQtThreadQueuedFn; @@ -311,6 +326,14 @@ impl cxx_qt::Threading for ffi::MyObject { pub struct MyObjectCxxQtThreadQueuedFn { inner: std::boxed::Box) + Send>, } +impl ::cxx_qt::Upcast<::cxx_qt::QObject> for ffi::MyObject { + unsafe fn upcast_ptr(this: *const Self) -> *const ::cxx_qt::QObject { + ffi::cxx_qt_ffi_MyObject_upcastPtr(this) + } + unsafe fn from_base_ptr(base: *const ::cxx_qt::QObject) -> *const Self { + ffi::cxx_qt_ffi_MyObject_downcastPtr(base) + } +} #[doc(hidden)] pub fn route_arguments_MyObject_0<'a>( arg0: i32, diff --git a/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.h b/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.h index ac35821e9..76dafdad2 100644 --- a/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.h +++ b/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include diff --git a/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.rs b/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.rs index 4d88d8da0..e1b6ad3aa 100644 --- a/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.rs +++ b/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.rs @@ -165,6 +165,17 @@ pub mod ffi { self_value: Pin<&mut MyObject>, ); } + extern "C++" { + #[doc(hidden)] + #[cxx_name = "upcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_MyObject_upcastPtr(thiz: *const MyObject) -> *const QStringListModel; + #[doc(hidden)] + #[cxx_name = "downcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_MyObject_downcastPtr(base: *const QStringListModel) + -> *const MyObject; + } extern "Rust" { #[cxx_name = "createRs"] #[namespace = "cxx_qt::multi_object::cxx_qt_MyObject"] @@ -281,6 +292,16 @@ pub mod ffi { self_value: Pin<&mut SecondObject>, ); } + extern "C++" { + #[doc(hidden)] + #[cxx_name = "upcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_SecondObject_upcastPtr(thiz: *const SecondObject) -> *const QObject; + #[doc(hidden)] + #[cxx_name = "downcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_SecondObject_downcastPtr(base: *const QObject) -> *const SecondObject; + } extern "Rust" { #[cxx_name = "createRs"] #[namespace = "second_object::cxx_qt_SecondObject"] @@ -317,6 +338,16 @@ pub mod ffi { #[namespace = "my_namespace"] type ThirdObjectRust; } + extern "C++" { + #[doc(hidden)] + #[cxx_name = "upcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_MyCxxName_upcastPtr(thiz: *const MyRustName) -> *const QObject; + #[doc(hidden)] + #[cxx_name = "downcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_MyCxxName_downcastPtr(base: *const QObject) -> *const MyRustName; + } extern "Rust" { #[cxx_name = "createRs"] #[namespace = "my_namespace::cxx_qt_MyRustName"] @@ -439,11 +470,12 @@ pub mod ffi { self_value: Pin<&mut ExternObject>, ); } + extern "C++" { + #[doc(hidden)] + #[namespace = ""] + type QObject = cxx_qt::QObject; + } } -impl cxx_qt::Upcast for ffi::MyObject {} -#[allow(unused_imports)] -#[allow(dead_code)] -use ffi::QStringListModel as _; impl ffi::MyObject { #[doc = "Getter for the Q_PROPERTY "] #[doc = "property_name"] @@ -590,6 +622,14 @@ cxx_qt::static_assertions::assert_eq_size!( cxx_qt::signalhandler::CxxQtSignalHandler, [usize; 2] ); +impl ::cxx_qt::Upcast for ffi::MyObject { + unsafe fn upcast_ptr(this: *const Self) -> *const ffi::QStringListModel { + ffi::cxx_qt_ffi_MyObject_upcastPtr(this) + } + unsafe fn from_base_ptr(base: *const ffi::QStringListModel) -> *const Self { + ffi::cxx_qt_ffi_MyObject_downcastPtr(base) + } +} #[doc(hidden)] #[allow(clippy::unnecessary_box_returns)] pub fn create_rs_MyObjectRust() -> std::boxed::Box { @@ -756,6 +796,14 @@ cxx_qt::static_assertions::assert_eq_size!( cxx_qt::signalhandler::CxxQtSignalHandler, [usize; 2] ); +impl ::cxx_qt::Upcast<::cxx_qt::QObject> for ffi::SecondObject { + unsafe fn upcast_ptr(this: *const Self) -> *const ::cxx_qt::QObject { + ffi::cxx_qt_ffi_SecondObject_upcastPtr(this) + } + unsafe fn from_base_ptr(base: *const ::cxx_qt::QObject) -> *const Self { + ffi::cxx_qt_ffi_SecondObject_downcastPtr(base) + } +} #[doc(hidden)] #[allow(clippy::unnecessary_box_returns)] pub fn create_rs_SecondObjectRust() -> std::boxed::Box { @@ -776,6 +824,14 @@ impl ::cxx_qt::CxxQtType for ffi::SecondObject { ffi::cxx_qt_ffi_SecondObject_unsafeRustMut(self) } } +impl ::cxx_qt::Upcast<::cxx_qt::QObject> for ffi::MyRustName { + unsafe fn upcast_ptr(this: *const Self) -> *const ::cxx_qt::QObject { + ffi::cxx_qt_ffi_MyCxxName_upcastPtr(this) + } + unsafe fn from_base_ptr(base: *const ::cxx_qt::QObject) -> *const Self { + ffi::cxx_qt_ffi_MyCxxName_downcastPtr(base) + } +} #[doc(hidden)] #[allow(clippy::unnecessary_box_returns)] pub fn create_rs_ThirdObjectRust() -> std::boxed::Box { diff --git a/crates/cxx-qt-gen/test_outputs/properties.h b/crates/cxx-qt-gen/test_outputs/properties.h index a75354ee1..ee9caf2b1 100644 --- a/crates/cxx-qt-gen/test_outputs/properties.h +++ b/crates/cxx-qt-gen/test_outputs/properties.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include diff --git a/crates/cxx-qt-gen/test_outputs/properties.rs b/crates/cxx-qt-gen/test_outputs/properties.rs index ab7ec97d8..683cf538f 100644 --- a/crates/cxx-qt-gen/test_outputs/properties.rs +++ b/crates/cxx-qt-gen/test_outputs/properties.rs @@ -389,6 +389,16 @@ mod ffi { self_value: Pin<&mut MyObject>, ); } + extern "C++" { + #[doc(hidden)] + #[cxx_name = "upcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_MyObject_upcastPtr(thiz: *const MyObject) -> *const QObject; + #[doc(hidden)] + #[cxx_name = "downcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_MyObject_downcastPtr(base: *const QObject) -> *const MyObject; + } extern "Rust" { #[cxx_name = "createRs"] #[namespace = "cxx_qt::my_object::cxx_qt_MyObject"] @@ -406,6 +416,11 @@ mod ffi { #[namespace = "rust::cxxqt1"] fn cxx_qt_ffi_MyObject_unsafeRustMut(outer: Pin<&mut MyObject>) -> Pin<&mut MyObjectRust>; } + extern "C++" { + #[doc(hidden)] + #[namespace = ""] + type QObject = cxx_qt::QObject; + } } impl ffi::MyObject { #[doc = "Getter for the Q_PROPERTY "] @@ -1037,6 +1052,14 @@ cxx_qt::static_assertions::assert_eq_size!( cxx_qt::signalhandler::CxxQtSignalHandler, [usize; 2] ); +impl ::cxx_qt::Upcast<::cxx_qt::QObject> for ffi::MyObject { + unsafe fn upcast_ptr(this: *const Self) -> *const ::cxx_qt::QObject { + ffi::cxx_qt_ffi_MyObject_upcastPtr(this) + } + unsafe fn from_base_ptr(base: *const ::cxx_qt::QObject) -> *const Self { + ffi::cxx_qt_ffi_MyObject_downcastPtr(base) + } +} #[doc(hidden)] #[allow(clippy::unnecessary_box_returns)] pub fn create_rs_MyObjectRust() -> std::boxed::Box { diff --git a/crates/cxx-qt-gen/test_outputs/qenum.h b/crates/cxx-qt-gen/test_outputs/qenum.h index 71e53b9e1..a81c424c2 100644 --- a/crates/cxx-qt-gen/test_outputs/qenum.h +++ b/crates/cxx-qt-gen/test_outputs/qenum.h @@ -3,6 +3,7 @@ #include #include #include +#include #include namespace cxx_qt::my_object { diff --git a/crates/cxx-qt-gen/test_outputs/qenum.rs b/crates/cxx-qt-gen/test_outputs/qenum.rs index 635eb886c..05bdfdfde 100644 --- a/crates/cxx-qt-gen/test_outputs/qenum.rs +++ b/crates/cxx-qt-gen/test_outputs/qenum.rs @@ -90,6 +90,16 @@ mod ffi { #[doc(hidden)] unsafe fn my_invokable(self: &MyObject, qenum: MyEnum, other_qenum: MyOtherEnum); } + extern "C++" { + #[doc(hidden)] + #[cxx_name = "upcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_MyObject_upcastPtr(thiz: *const MyObject) -> *const QObject; + #[doc(hidden)] + #[cxx_name = "downcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_MyObject_downcastPtr(base: *const QObject) -> *const MyObject; + } extern "Rust" { #[cxx_name = "createRs"] #[namespace = "cxx_qt::my_object::cxx_qt_MyObject"] @@ -124,6 +134,16 @@ mod ffi { #[namespace = "cxx_qt::my_object"] type InternalObject; } + extern "C++" { + #[doc(hidden)] + #[cxx_name = "upcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_CxxName_upcastPtr(thiz: *const MyRenamedObject) -> *const QObject; + #[doc(hidden)] + #[cxx_name = "downcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_CxxName_downcastPtr(base: *const QObject) -> *const MyRenamedObject; + } extern "Rust" { #[cxx_name = "createRs"] #[namespace = "cxx_qt::my_object::cxx_qt_MyRenamedObject"] @@ -143,6 +163,19 @@ mod ffi { outer: Pin<&mut MyRenamedObject>, ) -> Pin<&mut InternalObject>; } + extern "C++" { + #[doc(hidden)] + #[namespace = ""] + type QObject = cxx_qt::QObject; + } +} +impl ::cxx_qt::Upcast<::cxx_qt::QObject> for ffi::MyObject { + unsafe fn upcast_ptr(this: *const Self) -> *const ::cxx_qt::QObject { + ffi::cxx_qt_ffi_MyObject_upcastPtr(this) + } + unsafe fn from_base_ptr(base: *const ::cxx_qt::QObject) -> *const Self { + ffi::cxx_qt_ffi_MyObject_downcastPtr(base) + } } #[doc(hidden)] #[allow(clippy::unnecessary_box_returns)] @@ -164,6 +197,14 @@ impl ::cxx_qt::CxxQtType for ffi::MyObject { ffi::cxx_qt_ffi_MyObject_unsafeRustMut(self) } } +impl ::cxx_qt::Upcast<::cxx_qt::QObject> for ffi::MyRenamedObject { + unsafe fn upcast_ptr(this: *const Self) -> *const ::cxx_qt::QObject { + ffi::cxx_qt_ffi_CxxName_upcastPtr(this) + } + unsafe fn from_base_ptr(base: *const ::cxx_qt::QObject) -> *const Self { + ffi::cxx_qt_ffi_CxxName_downcastPtr(base) + } +} #[doc(hidden)] #[allow(clippy::unnecessary_box_returns)] pub fn create_rs_InternalObject() -> std::boxed::Box { diff --git a/crates/cxx-qt-gen/test_outputs/signals.h b/crates/cxx-qt-gen/test_outputs/signals.h index 4a1092750..bad91af1b 100644 --- a/crates/cxx-qt-gen/test_outputs/signals.h +++ b/crates/cxx-qt-gen/test_outputs/signals.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include diff --git a/crates/cxx-qt-gen/test_outputs/signals.rs b/crates/cxx-qt-gen/test_outputs/signals.rs index db9c4cc76..811cd257c 100644 --- a/crates/cxx-qt-gen/test_outputs/signals.rs +++ b/crates/cxx-qt-gen/test_outputs/signals.rs @@ -154,6 +154,16 @@ mod ffi { fourth: &'a QPoint, ); } + extern "C++" { + #[doc(hidden)] + #[cxx_name = "upcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_MyObject_upcastPtr(thiz: *const MyObject) -> *const QObject; + #[doc(hidden)] + #[cxx_name = "downcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn cxx_qt_ffi_MyObject_downcastPtr(base: *const QObject) -> *const MyObject; + } extern "Rust" { #[cxx_name = "createRs"] #[namespace = "cxx_qt::my_object::cxx_qt_MyObject"] @@ -201,6 +211,11 @@ mod ffi { self_value: Pin<&mut QTimer>, ); } + extern "C++" { + #[doc(hidden)] + #[namespace = ""] + type QObject = cxx_qt::QObject; + } } impl ffi::MyObject { #[doc = "Connect the given function pointer to the signal "] @@ -428,6 +443,14 @@ cxx_qt::static_assertions::assert_eq_size!( cxx_qt::signalhandler::CxxQtSignalHandler, [usize; 2] ); +impl ::cxx_qt::Upcast<::cxx_qt::QObject> for ffi::MyObject { + unsafe fn upcast_ptr(this: *const Self) -> *const ::cxx_qt::QObject { + ffi::cxx_qt_ffi_MyObject_upcastPtr(this) + } + unsafe fn from_base_ptr(base: *const ::cxx_qt::QObject) -> *const Self { + ffi::cxx_qt_ffi_MyObject_downcastPtr(base) + } +} #[doc(hidden)] #[allow(clippy::unnecessary_box_returns)] pub fn create_rs_MyObjectRust() -> std::boxed::Box { diff --git a/crates/cxx-qt-lib/include/core/qstringlist.h b/crates/cxx-qt-lib/include/core/qstringlist.h index f1f60fb3c..25f439c84 100644 --- a/crates/cxx-qt-lib/include/core/qstringlist.h +++ b/crates/cxx-qt-lib/include/core/qstringlist.h @@ -24,11 +24,6 @@ struct IsRelocatable : ::std::true_type namespace rust { namespace cxxqtlib1 { - -const QList& -qstringlistAsQListQStringRef(const QStringList& list); -QList& -qstringlistAsQListQStringRef(QStringList& list); QStringList qstringlistFromQListQString(const QList& list); QList diff --git a/crates/cxx-qt-lib/include/qml/qqmlapplicationengine.h b/crates/cxx-qt-lib/include/qml/qqmlapplicationengine.h index 2ca606f2d..d43f302c5 100644 --- a/crates/cxx-qt-lib/include/qml/qqmlapplicationengine.h +++ b/crates/cxx-qt-lib/include/qml/qqmlapplicationengine.h @@ -22,9 +22,6 @@ namespace cxxqtlib1 { ::std::unique_ptr qqmlapplicationengineNew(); -QQmlEngine& -qqmlapplicationengineAsQQmlEngine(QQmlApplicationEngine&); - } } diff --git a/crates/cxx-qt-lib/src/core/qstringlist.cpp b/crates/cxx-qt-lib/src/core/qstringlist.cpp index a5ea48a60..12382c657 100644 --- a/crates/cxx-qt-lib/src/core/qstringlist.cpp +++ b/crates/cxx-qt-lib/src/core/qstringlist.cpp @@ -36,18 +36,6 @@ static_assert(QTypeInfo::isRelocatable); namespace rust { namespace cxxqtlib1 { -const QList& -qstringlistAsQListQStringRef(const QStringList& list) -{ - return static_cast&>(list); -} - -QList& -qstringlistAsQListQStringRef(QStringList& list) -{ - return static_cast&>(list); -} - QStringList qstringlistFromQListQString(const QList& list) { diff --git a/crates/cxx-qt-lib/src/core/qstringlist.rs b/crates/cxx-qt-lib/src/core/qstringlist.rs index 7df0e0a40..8294b766d 100644 --- a/crates/cxx-qt-lib/src/core/qstringlist.rs +++ b/crates/cxx-qt-lib/src/core/qstringlist.rs @@ -2,9 +2,11 @@ // SPDX-FileContributor: Andrew Hayzen // // SPDX-License-Identifier: MIT OR Apache-2.0 +use crate::core::qstringlist::ffi::QList_QString; use crate::{QList, QString}; use core::mem::MaybeUninit; use cxx::{type_id, ExternType}; +use cxx_qt::Upcast; use std::fmt; use std::ops::{Deref, DerefMut}; @@ -26,6 +28,21 @@ mod ffi { include!("cxx-qt-lib/qstringlist.h"); type QStringList = super::QStringList; + include!("cxx-qt/casting.h"); + + #[doc(hidden)] + #[rust_name = "upcast_qstringlist"] + #[cxx_name = "upcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn upcast(thiz: *const QStringList) -> *const QList_QString; + + #[doc(hidden)] + #[rust_name = "downcast_qlist_qstring"] + #[cxx_name = "downcastPtr"] + #[namespace = "rust::cxxqt1"] + #[cfg(cxxqt_qt_version_at_least_6)] + unsafe fn downcast(base: *const QList_QString) -> *const QStringList; + /// Returns true if the list contains the string str; otherwise returns false. fn contains(self: &QStringList, str: &QString, cs: CaseSensitivity) -> bool; @@ -81,11 +98,6 @@ mod ffi { #[namespace = "rust::cxxqtlib1"] unsafe extern "C++" { - #[doc(hidden)] - #[rust_name = "qstringlist_as_qlist_qstring_ref"] - fn qstringlistAsQListQStringRef(list: &QStringList) -> &QList_QString; - #[rust_name = "qstringlist_as_qlist_qstring_ref_mut"] - fn qstringlistAsQListQStringRef(list: &mut QStringList) -> &mut QList_QString; #[doc(hidden)] #[rust_name = "qstringlist_from_qlist_qstring"] fn qstringlistFromQListQString(list: &QList_QString) -> QStringList; @@ -185,13 +197,29 @@ impl Deref for QStringList { type Target = QList; fn deref(&self) -> &Self::Target { - ffi::qstringlist_as_qlist_qstring_ref(self) + self.upcast() } } impl DerefMut for QStringList { fn deref_mut(&mut self) -> &mut Self::Target { - ffi::qstringlist_as_qlist_qstring_ref_mut(self) + self.upcast_mut() + } +} + +impl Upcast for QStringList { + unsafe fn upcast_ptr(this: *const Self) -> *const QList_QString { + ffi::upcast_qstringlist(this) + } + + #[cfg(cxxqt_qt_version_at_least_6)] + unsafe fn from_base_ptr(base: *const QList_QString) -> *const Self { + ffi::downcast_qlist_qstring(base) + } + + #[cfg(cxxqt_qt_version_major = "5")] + unsafe fn from_base_ptr(_base: *const QList_QString) -> *const Self { + std::ptr::null() } } diff --git a/crates/cxx-qt-lib/src/gui/qguiapplication.rs b/crates/cxx-qt-lib/src/gui/qguiapplication.rs index 62aabc406..f23abe12f 100644 --- a/crates/cxx-qt-lib/src/gui/qguiapplication.rs +++ b/crates/cxx-qt-lib/src/gui/qguiapplication.rs @@ -6,6 +6,7 @@ use crate::{QByteArray, QFont, QString, QStringList, QVector}; use core::pin::Pin; +use cxx_qt::Upcast; #[cxx::bridge] mod ffi { @@ -23,6 +24,23 @@ mod ffi { include!("cxx-qt-lib/qguiapplication.h"); type QGuiApplication; + + include!("cxx-qt-lib/qcoreapplication.h"); + type QCoreApplication; + + include!("cxx-qt/casting.h"); + + #[doc(hidden)] + #[rust_name = "upcast_qguiapplication"] + #[cxx_name = "upcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn upcast(thiz: *const QGuiApplication) -> *const QCoreApplication; + + #[doc(hidden)] + #[rust_name = "downcast_qcoreapplication"] + #[cxx_name = "downcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn downcast(base: *const QCoreApplication) -> *const QGuiApplication; } #[namespace = "rust::cxxqtlib1"] @@ -98,7 +116,19 @@ mod ffi { impl UniquePtr {} } -pub use ffi::QGuiApplication; +pub use ffi::{ + downcast_qcoreapplication, upcast_qguiapplication, QCoreApplication, QGuiApplication, +}; + +impl Upcast for QGuiApplication { + unsafe fn upcast_ptr(this: *const Self) -> *const QCoreApplication { + upcast_qguiapplication(this) + } + + unsafe fn from_base_ptr(base: *const QCoreApplication) -> *const Self { + downcast_qcoreapplication(base) + } +} impl QGuiApplication { /// Prepends path to the beginning of the library path list, diff --git a/crates/cxx-qt-lib/src/qml/qqmlapplicationengine.cpp b/crates/cxx-qt-lib/src/qml/qqmlapplicationengine.cpp index 1e858de98..8ed3e9aee 100644 --- a/crates/cxx-qt-lib/src/qml/qqmlapplicationengine.cpp +++ b/crates/cxx-qt-lib/src/qml/qqmlapplicationengine.cpp @@ -16,11 +16,5 @@ qqmlapplicationengineNew() return ::std::make_unique(); } -QQmlEngine& -qqmlapplicationengineAsQQmlEngine(QQmlApplicationEngine& engine) -{ - return static_cast(engine); -} - } } diff --git a/crates/cxx-qt-lib/src/qml/qqmlapplicationengine.rs b/crates/cxx-qt-lib/src/qml/qqmlapplicationengine.rs index 9e7b848a2..5b1612875 100644 --- a/crates/cxx-qt-lib/src/qml/qqmlapplicationengine.rs +++ b/crates/cxx-qt-lib/src/qml/qqmlapplicationengine.rs @@ -16,6 +16,20 @@ mod ffi { include!("cxx-qt-lib/qqmlapplicationengine.h"); type QQmlApplicationEngine; + include!("cxx-qt/casting.h"); + + #[doc(hidden)] + #[rust_name = "upcast_qqmlapplication_engine"] + #[cxx_name = "upcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn upcast(thiz: *const QQmlApplicationEngine) -> *const QQmlEngine; + + #[doc(hidden)] + #[rust_name = "downcast_qqml_engine"] + #[cxx_name = "downcastPtr"] + #[namespace = "rust::cxxqt1"] + unsafe fn downcast(base: *const QQmlEngine) -> *const QQmlApplicationEngine; + /// Adds path as a directory where the engine searches for installed modules in a URL-based directory structure. #[rust_name = "add_import_path"] fn addImportPath(self: Pin<&mut QQmlApplicationEngine>, path: &QString); @@ -71,12 +85,6 @@ mod ffi { #[doc(hidden)] #[rust_name = "qqmlapplicationengine_new"] fn qqmlapplicationengineNew() -> UniquePtr; - - #[doc(hidden)] - #[rust_name = "qqmlapplicationengine_as_qqmlengine"] - fn qqmlapplicationengineAsQQmlEngine( - ptr: Pin<&mut QQmlApplicationEngine>, - ) -> Pin<&mut QQmlEngine>; } // QQmlApplicationEngine is not a trivial to CXX and is not relocatable in Qt @@ -87,16 +95,21 @@ mod ffi { } use crate::QQmlEngine; -use core::pin::Pin; +use cxx_qt::Upcast; pub use ffi::QQmlApplicationEngine; -impl QQmlApplicationEngine { - /// Convert the existing [QQmlApplicationEngine] to a [QQmlEngine] - pub fn as_qqmlengine<'a>(self: Pin<&'a mut Self>) -> Pin<&'a mut QQmlEngine> { - ffi::qqmlapplicationengine_as_qqmlengine(self) +impl Upcast for QQmlApplicationEngine { + unsafe fn upcast_ptr(this: *const Self) -> *const QQmlEngine { + ffi::upcast_qqmlapplication_engine(this) + } + + unsafe fn from_base_ptr(base: *const QQmlEngine) -> *const Self { + ffi::downcast_qqml_engine(base) } +} +impl QQmlApplicationEngine { /// Create a new QQmlApplicationEngine pub fn new() -> cxx::UniquePtr { ffi::qqmlapplicationengine_new() diff --git a/crates/cxx-qt/build.rs b/crates/cxx-qt/build.rs index 21176344f..0f0f2956d 100644 --- a/crates/cxx-qt/build.rs +++ b/crates/cxx-qt/build.rs @@ -19,6 +19,7 @@ fn write_headers() { for file_path in [ "connection.h", + "casting.h", "signalhandler.h", "thread.h", "threading.h", @@ -40,7 +41,7 @@ fn main() { let mut builder = CxxQtBuilder::library(interface); let cpp_files = ["src/connection.cpp"]; - let rust_bridges = ["src/connection.rs"]; + let rust_bridges = ["src/connection.rs", "src/qobject.rs"]; for bridge in &rust_bridges { builder = builder.file(bridge); diff --git a/crates/cxx-qt/include/casting.h b/crates/cxx-qt/include/casting.h new file mode 100644 index 000000000..ecdaaabfd --- /dev/null +++ b/crates/cxx-qt/include/casting.h @@ -0,0 +1,28 @@ +// clang-format off +// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company +// clang-format on +// SPDX-FileContributor: Ben Ford +// +// SPDX-License-Identifier: MIT OR Apache-2.0 +#pragma once +#include + +namespace rust::cxxqt1 { + +template +const Base* +upcastPtr(const Sub* sub) +{ + static_assert(std::is_base_of_v); + return static_cast(sub); +} + +template +const Sub* +downcastPtr(const Base* base) +{ + static_assert(std::is_base_of_v); + return dynamic_cast(base); +} + +} diff --git a/crates/cxx-qt/src/lib.rs b/crates/cxx-qt/src/lib.rs index 66223338e..1dcf4f258 100644 --- a/crates/cxx-qt/src/lib.rs +++ b/crates/cxx-qt/src/lib.rs @@ -9,10 +9,13 @@ //! //! See the [book](https://kdab.github.io/cxx-qt/book/) for more information. +use std::ops::Deref; +use std::pin::Pin; use std::{fs::File, io::Write, path::Path}; mod connection; mod connectionguard; +mod qobject; #[doc(hidden)] pub mod signalhandler; mod threading; @@ -21,6 +24,7 @@ pub use cxx_qt_macro::bridge; pub use cxx_qt_macro::init_crate; pub use cxx_qt_macro::init_qml_module; pub use cxx_qt_macro::qobject; +pub use qobject::QObject; pub use connection::{ConnectionType, QMetaObjectConnection}; pub use connectionguard::QMetaObjectConnectionGuard; @@ -123,10 +127,101 @@ pub trait Threading: Sized { fn threading_drop(cxx_qt_thread: &mut CxxQtThread); } -/// Placeholder for upcasting objects, suppresses dead code warning -#[allow(dead_code)] -#[doc(hidden)] -pub trait Upcast {} +/// This trait is automatically implemented by CXX-Qt and you most likely do not need to manually implement it. +/// Allows upcasting to either [QObject] or the provided base class of a type. +/// Will not be implemented if no types inherit from [QObject] or have the `#[base = T]` attribute. +pub trait Upcast { + #[doc(hidden)] + /// # Safety + /// + /// Internal function, Should probably not be implemented manually unless you're absolutely sure you need it. + /// Automatically available for types in RustQt blocks in [cxx_qt::bridge](bridge)s. + /// Upcasts a pointer to `Self` to a pointer to the base class `T`. + /// > Note: Internal implementation uses `static_cast`. + unsafe fn upcast_ptr(this: *const Self) -> *const T; + + #[doc(hidden)] + /// # Safety + /// + /// Internal function, Should probably not be implemented manually unless you're absolutely sure you need it. + /// Automatically available for types in RustQt blocks in [cxx_qt::bridge](bridge)s. + /// Downcasts a pointer to base class `T` to a pointer to `Self`. + /// Return a null pointer if `Self` is not actually a child of base. + /// > Note: Internal implementation uses `dynamic_cast`. + unsafe fn from_base_ptr(base: *const T) -> *const Self; + + /// Upcast a reference to self to a reference to the base class + fn upcast(&self) -> &T { + let ptr = self as *const Self; + unsafe { + let base = Self::upcast_ptr(ptr); + &*base + } + } + + /// Upcast a mutable reference to sell to a mutable reference to the base class + fn upcast_mut(&mut self) -> &mut T { + let ptr = self as *const Self; + unsafe { + let base = Self::upcast_ptr(ptr) as *mut T; + &mut *base + } + } + + /// Upcast a pinned mutable reference to self to a pinned mutable reference to the base class + fn upcast_pin(self: Pin<&mut Self>) -> Pin<&mut T> { + let this = self.deref() as *const Self; + unsafe { + let base = Self::upcast_ptr(this) as *mut T; + Pin::new_unchecked(&mut *base) + } + } +} + +/// This trait is automatically implemented by CXX-Qt and you most likely do not need to manually implement it. +/// Trait for downcasting to a subclass, provided the subclass implements [Upcast] to this type. +/// Returns `None` in cases where `Sub` isn't a child class of `Self`. +pub trait Downcast: Sized { + /// Try to downcast to a subclass of this type, given that the subclass upcasts to this type + fn downcast>(&self) -> Option<&Sub> { + unsafe { + let ptr = Sub::from_base_ptr(self as *const Self); + if ptr.is_null() { + None + } else { + Some(&*ptr) + } + } + } + + /// Try to downcast mutably to a subclass of this, given that the subclass upcasts to this type + fn downcast_mut>(&mut self) -> Option<&mut Sub> { + unsafe { + let ptr = Sub::from_base_ptr(self as *const Self) as *mut Sub; + if ptr.is_null() { + None + } else { + Some(&mut *ptr) + } + } + } + + /// Try to downcast a pin to a pinned subclass of this, given that the subclass upcasts to this type + fn downcast_pin>(self: Pin<&mut Self>) -> Option> { + let this = self.deref() as *const Self; + unsafe { + let ptr = Sub::from_base_ptr(this) as *mut Sub; + if ptr.is_null() { + None + } else { + Some(Pin::new_unchecked(&mut *ptr)) + } + } + } +} + +/// Automatic implementation of Downcast for any applicable types +impl Downcast for T {} /// This trait can be implemented on any [CxxQtType] to define a /// custom constructor in C++ for the QObject. diff --git a/crates/cxx-qt/src/qobject.rs b/crates/cxx-qt/src/qobject.rs new file mode 100644 index 000000000..a1e0b6610 --- /dev/null +++ b/crates/cxx-qt/src/qobject.rs @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company +// SPDX-FileContributor: Ben Ford +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! QObject module + +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + // TODO! Implement QObject wrapper properly + include!(); + /// QObject type + type QObject; + + #[cxx_name = "dumpObjectInfo"] + /// Dump Object info method, added so that upcast methods can be tested. + fn dump_object_info(&self); + } +} + +pub use ffi::QObject; diff --git a/examples/cargo_without_cmake/src/main.rs b/examples/cargo_without_cmake/src/main.rs index 56fb170e4..12bab6719 100644 --- a/examples/cargo_without_cmake/src/main.rs +++ b/examples/cargo_without_cmake/src/main.rs @@ -14,6 +14,7 @@ // ANCHOR: book_cargo_imports pub mod cxxqt_object; +use cxx_qt::Upcast; use cxx_qt_lib::{QGuiApplication, QQmlApplicationEngine, QUrl}; // ANCHOR_END: book_cargo_imports @@ -31,7 +32,7 @@ fn main() { if let Some(engine) = engine.as_mut() { // Listen to a signal from the QML Engine engine - .as_qqmlengine() + .upcast_pin() .on_quit(|_| { println!("QML Quit!"); }) diff --git a/examples/qml_multi_crates/rust/main/src/main.rs b/examples/qml_multi_crates/rust/main/src/main.rs index 8354191c3..d39cd93c3 100644 --- a/examples/qml_multi_crates/rust/main/src/main.rs +++ b/examples/qml_multi_crates/rust/main/src/main.rs @@ -5,6 +5,7 @@ extern crate qml_multi_crates; +use cxx_qt::Upcast; use cxx_qt_lib::{QGuiApplication, QQmlApplicationEngine, QUrl}; fn main() { @@ -22,7 +23,7 @@ fn main() { if let Some(engine) = engine.as_mut() { // Listen to a signal from the QML Engine engine - .as_qqmlengine() + .upcast_pin() .on_quit(|_| { println!("QML Quit!"); })