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

Implement automatic implementations of up and downcasting #1159

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
6 changes: 6 additions & 0 deletions crates/cxx-qt-gen/src/generator/cpp/qobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,12 @@ impl GeneratedCppQObject {
class_initializers.push(initializer);
}

// Include casting header
let mut result = GeneratedCppQObjectBlocks::default();
result.includes.insert("#include <cxx-qt/casting.h>".into());

generated.blocks.append(&mut result);

generated.blocks.append(&mut constructor::generate(
&generated,
&structured_qobject.constructors,
Expand Down
33 changes: 29 additions & 4 deletions crates/cxx-qt-gen/src/generator/rust/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -91,6 +93,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::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,
Expand Down Expand Up @@ -139,7 +164,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]
Expand All @@ -159,7 +184,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]
Expand All @@ -179,6 +204,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);
}
}
111 changes: 76 additions & 35 deletions crates/cxx-qt-gen/src/generator/rust/qobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -18,8 +19,8 @@ use crate::{
},
naming::TypeNames,
};
use quote::quote;
use syn::{Ident, Result};
use quote::{format_ident, quote};
use syn::{parse_quote, Result};

impl GeneratedRustFragment {
// Might need to be refactored to use a StructuredQObject instead (confirm with Leon)
Expand All @@ -33,11 +34,7 @@ impl GeneratedRustFragment {
let namespace_idents = NamespaceName::from(qobject);
let mut generated = Self::default();

generated.append(&mut generate_qobject_definitions(
&qobject_names,
qobject.base_class.clone(),
type_names,
)?);
generated.append(&mut generate_qobject_definitions(&qobject_names)?);

// Generate methods for the properties, invokables, signals
generated.append(&mut generate_rust_properties(
Expand Down Expand Up @@ -86,6 +83,62 @@ impl GeneratedRustFragment {
)?);
}

// Generate casting impl
let mut blocks = GeneratedRustFragment::default();
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::qobject}),
); // TODO! is this default module here causing the issues in the threading examples

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();

//TODO! placeholder, use the right dynamic_cast function in from_base_ptr
let fragment = RustFragmentPair {
cxx_bridge: vec![quote! {
extern "C++" {
#[doc(hidden)]
#(#upcast_fn_attrs)*
unsafe fn #upcast_fn(thiz: *const #struct_name_unqualified) -> *const #base_unqualified;
}
}],
implementation: vec![
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 T) -> Option<*const Self> {
None
}
}
},
quote! {
impl ::cxx_qt::Downcast for #struct_name {}
},
],
};
blocks
.cxx_mod_contents
.append(&mut fragment.cxx_bridge_as_items()?);
blocks
.cxx_qt_mod_contents
.append(&mut fragment.implementation_as_items()?);
generated.append(&mut blocks);

generated.append(&mut constructor::generate(
&structured_qobject.constructors,
&qobject_names,
Expand All @@ -100,11 +153,7 @@ impl GeneratedRustFragment {
}

/// Generate the C++ and Rust CXX definitions for the QObject
fn generate_qobject_definitions(
qobject_idents: &QObjectNames,
base: Option<Ident>,
type_names: &TypeNames,
) -> Result<GeneratedRustFragment> {
fn generate_qobject_definitions(qobject_idents: &QObjectNames) -> Result<GeneratedRustFragment> {
let mut generated = GeneratedRustFragment::default();
let cpp_class_name_rust = &qobject_idents.name.rust_unqualified();
let cpp_class_name_cpp = &qobject_idents.name.cxx_unqualified();
Expand All @@ -123,25 +172,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![
quote! { 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
quote! {
#[allow(unused_imports)]
#[allow(dead_code)]
use #base_name as _;
},
]
} else {
vec![]
};

let fragment = RustFragmentPair {
cxx_bridge: vec![
quote! {
Expand All @@ -168,7 +198,7 @@ fn generate_qobject_definitions(
}
},
],
implementation: base_upcast,
implementation: vec![],
};

generated
Expand Down Expand Up @@ -224,7 +254,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! {
Expand Down Expand Up @@ -259,6 +289,17 @@ 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;
}
},
);
assert_tokens_eq(
&rust.cxx_mod_contents[4],
quote! {
extern "Rust" {
#[cxx_name = "createRs"]
Expand All @@ -268,7 +309,7 @@ mod tests {
},
);
assert_tokens_eq(
&rust.cxx_mod_contents[4],
&rust.cxx_mod_contents[5],
quote! {
unsafe extern "C++" {
#[doc(hidden)]
Expand All @@ -279,7 +320,7 @@ mod tests {
},
);
assert_tokens_eq(
&rust.cxx_mod_contents[5],
&rust.cxx_mod_contents[6],
quote! {
unsafe extern "C++" {
#[doc(hidden)]
Expand Down
1 change: 1 addition & 0 deletions crates/cxx-qt-gen/src/writer/cpp/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ mod tests {
let expected = indoc! {r#"
#pragma once

#include <cxx-qt/casting.h>
#include <cxx-qt/type.h>

class MyObject;
Expand Down
1 change: 1 addition & 0 deletions crates/cxx-qt-gen/test_outputs/inheritance.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <cxx-qt/casting.h>
#include <cxx-qt/type.h>

class MyObject;
Expand Down
20 changes: 16 additions & 4 deletions crates/cxx-qt-gen/test_outputs/inheritance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ 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;
}
extern "Rust" {
#[cxx_name = "createRs"]
#[namespace = "cxx_qt_MyObject"]
Expand All @@ -78,10 +85,15 @@ mod inheritance {
fn cxx_qt_ffi_MyObject_unsafeRustMut(outer: Pin<&mut MyObject>) -> Pin<&mut MyObjectRust>;
}
}
impl cxx_qt::Upcast<inheritance::QAbstractItemModel> for inheritance::MyObject {}
#[allow(unused_imports)]
#[allow(dead_code)]
use inheritance::QAbstractItemModel as _;
impl ::cxx_qt::Upcast<inheritance::QAbstractItemModel> 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 T) -> Option<*const Self> {
None
}
}
impl ::cxx_qt::Downcast for inheritance::MyObject {}
#[doc(hidden)]
pub fn create_rs_MyObjectRust() -> std::boxed::Box<MyObjectRust> {
std::boxed::Box::new(core::default::Default::default())
Expand Down
1 change: 1 addition & 0 deletions crates/cxx-qt-gen/test_outputs/invokables.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <cxx-qt/casting.h>
#include <cxx-qt/threading.h>
#include <cxx-qt/type.h>

Expand Down
20 changes: 20 additions & 0 deletions crates/cxx-qt-gen/test_outputs/invokables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@ 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;
}
#[namespace = "cxx_qt::my_object::cxx_qt_MyObject"]
#[cxx_name = "CxxQtConstructorArguments0"]
#[doc(hidden)]
Expand Down Expand Up @@ -246,6 +252,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::QObject;
}
}
impl cxx_qt::Threading for ffi::MyObject {
type BoxedQueuedFn = MyObjectCxxQtThreadQueuedFn;
Expand Down Expand Up @@ -292,6 +303,15 @@ impl cxx_qt::Threading for ffi::MyObject {
pub struct MyObjectCxxQtThreadQueuedFn {
inner: std::boxed::Box<dyn FnOnce(core::pin::Pin<&mut ffi::MyObject>) + Send>,
}
impl ::cxx_qt::Upcast<::cxx_qt::qobject::QObject> for ffi::MyObject {
unsafe fn upcast_ptr(this: *const Self) -> *const ::cxx_qt::qobject::QObject {
ffi::cxx_qt_ffi_MyObject_upcastPtr(this)
}
unsafe fn from_base_ptr(base: *const T) -> Option<*const Self> {
None
}
}
impl ::cxx_qt::Downcast for ffi::MyObject {}
#[doc(hidden)]
pub fn route_arguments_MyObject_0<'a>(
arg0: i32,
Expand Down
1 change: 1 addition & 0 deletions crates/cxx-qt-gen/test_outputs/passthrough_and_naming.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <cxx-qt/casting.h>
#include <cxx-qt/signalhandler.h>
#include <cxx-qt/type.h>

Expand Down
Loading
Loading