Skip to content
Merged
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
3 changes: 2 additions & 1 deletion book/src/concepts/casting.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ SPDX-License-Identifier: MIT OR Apache-2.0

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,
Currently, this is supported for objects in both `extern "RustQt"` *and* `extern "C++Qt"` blocks, which have either a `#[qobject]` attribute,
or a `#[base = T]` attribute. see [here](../bridge/attributes.md) for more details on these attributes.
> Note: Types in "C++Qt" blocks are **required** to have the `#[qobject]` attribute

## Accessing the base class

Expand Down
29 changes: 26 additions & 3 deletions crates/cxx-qt-gen/src/generator/cpp/externcxxqt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ use crate::{
use std::collections::BTreeSet;
use syn::Result;

#[derive(Default)]
#[derive(Default, Debug)]
pub struct GeneratedCppExternCxxQtBlocks {
/// List of includes
pub includes: BTreeSet<String>,
/// List of forward declares before the class and include of the generated CXX header
pub forward_declares: Vec<String>,
/// Base class of the QObject
pub base_classes: Vec<String>,
/// List of fragments
pub fragments: Vec<CppFragment>,
}
Expand All @@ -38,9 +40,30 @@ pub fn generate(
includes: data.includes,
forward_declares: data.forward_declares,
fragments: data.fragments,
..Default::default()
};
out.push(block);
}
let mut generated = GeneratedCppExternCxxQtBlocks {
base_classes: vec![],
..Default::default()
};

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

out.push(result);

for qobject in &block.qobjects {
let base_class = if let Some(ident) = &qobject.base_class {
type_names.lookup(ident)?.cxx_qualified()
} else {
"QObject".to_string()
};
generated.base_classes.push(base_class);
}
out.push(generated);
}

Ok(out)
Expand Down Expand Up @@ -78,7 +101,7 @@ mod tests {
assert!(generate(&blocks, &TypeNames::default(), &opt).is_err());

let generated = generate(&blocks, &TypeNames::mock(), &opt).unwrap();
assert_eq!(generated.len(), 2);
assert_eq!(generated.len(), 4);
}

#[test]
Expand All @@ -103,6 +126,6 @@ mod tests {
type_names.mock_insert("ObjRust", None, Some("ObjCpp"), Some("mynamespace"));

let generated = generate(&blocks, &type_names, &GeneratedOpt::default()).unwrap();
assert_eq!(generated.len(), 1);
assert_eq!(generated.len(), 3);
}
}
9 changes: 9 additions & 0 deletions crates/cxx-qt-gen/src/generator/naming/qobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-FileContributor: Andrew Hayzen <andrew.hayzen@kdab.com>
//
// SPDX-License-Identifier: MIT OR Apache-2.0
use crate::parser::externqobject::ParsedExternQObject;
use crate::{
naming::{Name, TypeNames},
parser::qobject::ParsedQObject,
Expand All @@ -28,6 +29,14 @@ impl QObjectNames {
Self::from_name_and_ident(&qobject.name, &qobject.rust_type, type_names)
}

/// For a given ExternQObject, create the names associated with it for generation.
pub fn from_extern_qobject(
qobject: &ParsedExternQObject,
type_names: &TypeNames,
) -> Result<Self> {
Self::from_name_and_ident(&qobject.name, qobject.name.rust_unqualified(), type_names)
}

/// From the QObject name and Rust struct ident, create the names needed for generation.
pub fn from_name_and_ident(
qobject_name: &Name,
Expand Down
51 changes: 33 additions & 18 deletions crates/cxx-qt-gen/src/generator/rust/externcxxqt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-FileContributor: Andrew Hayzen <andrew.hayzen@kdab.com>
//
// SPDX-License-Identifier: MIT OR Apache-2.0
use crate::generator::naming::qobject::QObjectNames;
use crate::{
generator::rust::{fragment::GeneratedRustFragment, signals::generate_rust_signal},
naming::TypeNames,
Expand All @@ -25,10 +26,20 @@ impl GeneratedRustFragment {
// Add the pass through blocks
let unsafety = &extern_cxxqt_block.unsafety;
let items = &extern_cxxqt_block.passthrough_items;
let types = &extern_cxxqt_block
let mut generated = extern_cxxqt_block
.qobjects
.iter()
.map(|ty| {
.map(|ty| -> Result<GeneratedRustFragment> {
let mut generated = vec![];
let qobject_names = QObjectNames::from_extern_qobject(ty, type_names)?;

generated.push(GeneratedRustFragment::generate_casting_impl(
&qobject_names,
type_names,
&ty.name,
&ty.base_class,
)?);

let namespace = if let Some(namespace) = &ty.name.namespace() {
quote! { #[namespace = #namespace ] }
} else {
Expand Down Expand Up @@ -58,24 +69,28 @@ impl GeneratedRustFragment {
.iter()
.filter(|attr| path_compare_str(attr.meta.path(), &["doc"]))
.collect();
quote! {
#namespace
#cxx_name
#(#cfgs)*
#(#docs)*
#vis type #ident;
}
generated.push(GeneratedRustFragment::from_cxx_item(parse_quote! {
#extern_block_namespace
#unsafety extern "C++" {
#namespace
#cxx_name
#(#cfgs)*
#(#docs)*
#vis type #ident;
}
}));
Ok(GeneratedRustFragment::flatten(generated))
})
.collect::<Vec<_>>();
.collect::<Result<Vec<_>>>()?;

let mut generated = vec![GeneratedRustFragment::from_cxx_item(parse_quote! {
#extern_block_namespace
#unsafety extern "C++" {
#(#items)*

#(#types)*
}
})];
if !items.is_empty() {
generated.push(GeneratedRustFragment::from_cxx_item(parse_quote! {
#extern_block_namespace
#unsafety extern "C++" {
#(#items)*
}
}));
}

// Build the signals
for signal in &extern_cxxqt_block.signals {
Expand Down
74 changes: 73 additions & 1 deletion crates/cxx-qt-gen/src/generator/rust/fragment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
//
// SPDX-License-Identifier: MIT OR Apache-2.0

use syn::Item;
use crate::generator::naming::qobject::QObjectNames;
use crate::naming::{Name, TypeNames};
use proc_macro2::Ident;
use quote::format_ident;
use syn::{parse_quote, Item, Result};

#[derive(Default, Eq, PartialEq, Debug)]
pub struct GeneratedRustFragment {
Expand All @@ -19,6 +23,74 @@ impl GeneratedRustFragment {
self.cxx_qt_mod_contents.extend(other.cxx_qt_mod_contents);
}

pub fn qobject_import() -> Self {
Self {
cxx_mod_contents: vec![parse_quote! {
extern "C++" {
#[doc(hidden)]
#[namespace=""]
type QObject = cxx_qt::QObject;
}
}],
cxx_qt_mod_contents: vec![],
}
}

/// Generate the required trait function implementations for casting QObjects
pub fn generate_casting_impl(
qobject_names: &QObjectNames,
type_names: &TypeNames,
type_name: &Name,
base_class: &Option<Ident>,
) -> Result<Self> {
// Create name from base ident
let base = 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 = type_name.rust_qualified();
let struct_name_unqualified = type_name.rust_unqualified();

// Create ffi function names
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();

Ok(Self {
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)
}
}
}],
})
}

// Create a singular GeneratedRustFragment from a Vector of multiple
pub fn flatten(others: Vec<Self>) -> Self {
let mut this = Self::default();
Expand Down
38 changes: 19 additions & 19 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,8 @@ pub mod signals;
pub mod threading;

use crate::generator::{rust::fragment::GeneratedRustFragment, structuring};
use crate::parser::{parameter::ParsedFunctionParameter, qobject::ParsedQObject, Parser};
use crate::parser::cxxqtdata::ParsedCxxQtData;
use crate::parser::{parameter::ParsedFunctionParameter, Parser};
use proc_macro2::{Ident, TokenStream};
use quote::quote;
use syn::{parse_quote, Item, ItemMod, Result};
Expand Down Expand Up @@ -60,7 +61,9 @@ 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)]);
if let Some(qobject_import) = add_qobject_import(&parser.cxx_qt_data) {
fragments.push(qobject_import);
}

let vis = &passthrough_mod.vis;
let ident = &passthrough_mod.module_ident;
Expand Down Expand Up @@ -97,26 +100,23 @@ impl GeneratedRustBlocks {
}
}

fn add_qobject_import(qobjects: &[ParsedQObject]) -> GeneratedRustFragment {
let includes = qobjects
// Generate a type declaration for `QObject` if necessary
fn add_qobject_import(cxx_qt_data: &ParsedCxxQtData) -> Option<GeneratedRustFragment> {
let includes = cxx_qt_data
.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![],
}
if includes
|| cxx_qt_data
.extern_cxxqt_blocks
.iter()
.filter(|block| !block.qobjects.is_empty())
.count()
> 0
{
Some(GeneratedRustFragment::qobject_import())
} else {
GeneratedRustFragment {
cxx_mod_contents: vec![],
cxx_qt_mod_contents: vec![],
}
None
}
}

Expand Down
Loading
Loading