Skip to content

Commit

Permalink
cxx-qt-gen: add support for closures in signals
Browse files Browse the repository at this point in the history
Also reuse same code path for RustQt and C++Qt signals.

Closes KDAB#595
  • Loading branch information
ahayzen-kdab committed Nov 15, 2023
1 parent c193ad3 commit c3a0272
Show file tree
Hide file tree
Showing 24 changed files with 3,509 additions and 834 deletions.
26 changes: 12 additions & 14 deletions crates/cxx-qt-gen/src/generator/cpp/externcxxqt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// SPDX-License-Identifier: MIT OR Apache-2.0

use crate::{
generator::cpp::signal::generate_cpp_free_signal,
generator::cpp::signal::generate_cpp_signal,
parser::{externcxxqt::ParsedExternCxxQt, mappings::ParsedCxxMappings},
CppFragment,
};
Expand All @@ -15,10 +15,10 @@ use syn::Result;
pub struct GeneratedCppExternCxxQtBlocks {
/// List of includes
pub includes: BTreeSet<String>,
/// List of methods
pub method: CppFragment,
/// Namespace of the method block
pub namespace: String,
/// List of forward declares before the class and include of the generated CXX header
pub forward_declares: Vec<String>,
/// List of fragments
pub fragments: Vec<CppFragment>,
}

pub fn generate(
Expand All @@ -29,7 +29,13 @@ pub fn generate(

for block in blocks {
for signal in &block.signals {
out.push(generate_cpp_free_signal(signal, cxx_mappings)?);
let mut block = GeneratedCppExternCxxQtBlocks::default();
let data = generate_cpp_signal(signal, &signal.qobject_ident, cxx_mappings)?;
block.includes = data.includes;
block.forward_declares = data.forward_declares;
block.fragments = data.fragments;
debug_assert!(data.methods.is_empty());
out.push(block);
}
}

Expand Down Expand Up @@ -58,9 +64,6 @@ mod tests {
.unwrap()];
let generated = generate(&blocks, &ParsedCxxMappings::default()).unwrap();
assert_eq!(generated.len(), 2);

assert_eq!(generated[0].namespace, "rust::cxxqtgen1::externcxxqt");
assert_eq!(generated[1].namespace, "rust::cxxqtgen1::externcxxqt");
}

#[test]
Expand All @@ -86,10 +89,5 @@ mod tests {

let generated = generate(&blocks, &cxx_mappings).unwrap();
assert_eq!(generated.len(), 1);

assert_eq!(
generated[0].namespace,
"rust::cxxqtgen1::externcxxqt::mynamespace"
);
}
}
170 changes: 137 additions & 33 deletions crates/cxx-qt-gen/src/generator/cpp/property/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ mod tests {
assert_str_eq!(generated.metaobjects[1], "Q_PROPERTY(::std::unique_ptr<QColor> opaqueProperty READ getOpaqueProperty WRITE setOpaqueProperty NOTIFY opaquePropertyChanged)");

// methods
assert_eq!(generated.methods.len(), 8);
assert_eq!(generated.methods.len(), 6);
let (header, source) = if let CppFragment::Pair { header, source } = &generated.methods[0] {
(header, source)
} else {
Expand Down Expand Up @@ -179,64 +179,133 @@ mod tests {
};
assert_str_eq!(header, "Q_SIGNAL void trivialPropertyChanged();");

let (header, source) = if let CppFragment::Pair { header, source } = &generated.methods[5] {
let header = if let CppFragment::Header(header) = &generated.methods[5] {
header
} else {
panic!("Expected header!")
};
assert_str_eq!(header, "Q_SIGNAL void opaquePropertyChanged();");

assert_eq!(generated.fragments.len(), 2);
let (header, source) = if let CppFragment::Pair { header, source } = &generated.fragments[0]
{
(header, source)
} else {
panic!("Expected Pair")
};
assert_str_eq!(
header,
"::QMetaObject::Connection trivialPropertyChangedConnect(::rust::Fn<void(MyObject&)> func, ::Qt::ConnectionType type);"
indoc! {r#"
namespace rust::cxxqtgen1 {
::QMetaObject::Connection
MyObject_trivialPropertyChangedConnect(MyObject& self, ::rust::cxxqtgen1::MyObjectCxxQtSignalHandlertrivialPropertyChanged closure, ::Qt::ConnectionType type);
} // namespace rust::cxxqtgen1
"#}
);
assert_str_eq!(
source,
indoc! {r#"
// Define namespace otherwise we hit a GCC bug
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480
namespace rust::cxxqtlib1 {
template <>
SignalHandler<::rust::cxxqtgen1::MyObjectCxxQtSignalParamstrivialPropertyChanged *>::~SignalHandler()
{
if (data[0] == nullptr && data[1] == nullptr)
{
return;
}
drop_MyObject_signal_handler_trivialPropertyChanged(::std::move(*this));
}
template <>
template <>
void SignalHandler<::rust::cxxqtgen1::MyObjectCxxQtSignalParamstrivialPropertyChanged *>::operator()<MyObject&>(MyObject& self)
{
call_MyObject_signal_handler_trivialPropertyChanged(*this, self);
}
static_assert(alignof(SignalHandler<::rust::cxxqtgen1::MyObjectCxxQtSignalParamstrivialPropertyChanged *>) <= alignof(::std::size_t), "unexpected aligment");
static_assert(sizeof(SignalHandler<::rust::cxxqtgen1::MyObjectCxxQtSignalParamstrivialPropertyChanged *>) == sizeof(::std::size_t[2]), "unexpected size");
} // namespace rust::cxxqtlib1
namespace rust::cxxqtgen1 {
::QMetaObject::Connection
MyObject::trivialPropertyChangedConnect(::rust::Fn<void(MyObject&)> func, ::Qt::ConnectionType type)
MyObject_trivialPropertyChangedConnect(MyObject& self, ::rust::cxxqtgen1::MyObjectCxxQtSignalHandlertrivialPropertyChanged closure, ::Qt::ConnectionType type)
{
return ::QObject::connect(this,
return ::QObject::connect(
&self,
&MyObject::trivialPropertyChanged,
this,
[&, func = ::std::move(func)]() {
const ::rust::cxxqtlib1::MaybeLockGuard<MyObject> guard(*this);
func(*this);
&self,
[&, closure = ::std::move(closure)]() mutable {
const ::rust::cxxqtlib1::MaybeLockGuard<MyObject> guard(self);
closure.template operator()<MyObject&>(self);
},
type);
}
} // namespace rust::cxxqtgen1
"#}
);

let header = if let CppFragment::Header(header) = &generated.methods[6] {
header
} else {
panic!("Expected header!")
};
assert_str_eq!(header, "Q_SIGNAL void opaquePropertyChanged();");

let (header, source) = if let CppFragment::Pair { header, source } = &generated.methods[7] {
let (header, source) = if let CppFragment::Pair { header, source } = &generated.fragments[1]
{
(header, source)
} else {
panic!("Expected Pair")
};
assert_str_eq!(
header,
"::QMetaObject::Connection opaquePropertyChangedConnect(::rust::Fn<void(MyObject&)> func, ::Qt::ConnectionType type);"
indoc! {r#"
namespace rust::cxxqtgen1 {
::QMetaObject::Connection
MyObject_opaquePropertyChangedConnect(MyObject& self, ::rust::cxxqtgen1::MyObjectCxxQtSignalHandleropaquePropertyChanged closure, ::Qt::ConnectionType type);
} // namespace rust::cxxqtgen1
"#}
);
assert_str_eq!(
source,
indoc! {r#"
// Define namespace otherwise we hit a GCC bug
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480
namespace rust::cxxqtlib1 {
template <>
SignalHandler<::rust::cxxqtgen1::MyObjectCxxQtSignalParamsopaquePropertyChanged *>::~SignalHandler()
{
if (data[0] == nullptr && data[1] == nullptr)
{
return;
}
drop_MyObject_signal_handler_opaquePropertyChanged(::std::move(*this));
}
template <>
template <>
void SignalHandler<::rust::cxxqtgen1::MyObjectCxxQtSignalParamsopaquePropertyChanged *>::operator()<MyObject&>(MyObject& self)
{
call_MyObject_signal_handler_opaquePropertyChanged(*this, self);
}
static_assert(alignof(SignalHandler<::rust::cxxqtgen1::MyObjectCxxQtSignalParamsopaquePropertyChanged *>) <= alignof(::std::size_t), "unexpected aligment");
static_assert(sizeof(SignalHandler<::rust::cxxqtgen1::MyObjectCxxQtSignalParamsopaquePropertyChanged *>) == sizeof(::std::size_t[2]), "unexpected size");
} // namespace rust::cxxqtlib1
namespace rust::cxxqtgen1 {
::QMetaObject::Connection
MyObject::opaquePropertyChangedConnect(::rust::Fn<void(MyObject&)> func, ::Qt::ConnectionType type)
MyObject_opaquePropertyChangedConnect(MyObject& self, ::rust::cxxqtgen1::MyObjectCxxQtSignalHandleropaquePropertyChanged closure, ::Qt::ConnectionType type)
{
return ::QObject::connect(this,
return ::QObject::connect(
&self,
&MyObject::opaquePropertyChanged,
this,
[&, func = ::std::move(func)]() {
const ::rust::cxxqtlib1::MaybeLockGuard<MyObject> guard(*this);
func(*this);
&self,
[&, closure = ::std::move(closure)]() mutable {
const ::rust::cxxqtlib1::MaybeLockGuard<MyObject> guard(self);
closure.template operator()<MyObject&>(self);
},
type);
}
} // namespace rust::cxxqtgen1
"#}
);

Expand Down Expand Up @@ -304,7 +373,7 @@ mod tests {
assert_str_eq!(generated.metaobjects[0], "Q_PROPERTY(A1 mappedProperty READ getMappedProperty WRITE setMappedProperty NOTIFY mappedPropertyChanged)");

// methods
assert_eq!(generated.methods.len(), 4);
assert_eq!(generated.methods.len(), 3);
let (header, source) = if let CppFragment::Pair { header, source } = &generated.methods[0] {
(header, source)
} else {
Expand Down Expand Up @@ -347,30 +416,65 @@ mod tests {
};
assert_str_eq!(header, "Q_SIGNAL void mappedPropertyChanged();");

let (header, source) = if let CppFragment::Pair { header, source } = &generated.methods[3] {
assert_eq!(generated.fragments.len(), 1);
let (header, source) = if let CppFragment::Pair { header, source } = &generated.fragments[0]
{
(header, source)
} else {
panic!("Expected Pair")
};
assert_str_eq!(
header,
"::QMetaObject::Connection mappedPropertyChangedConnect(::rust::Fn<void(MyObject&)> func, ::Qt::ConnectionType type);"
indoc! {r#"
namespace rust::cxxqtgen1 {
::QMetaObject::Connection
MyObject_mappedPropertyChangedConnect(MyObject& self, ::rust::cxxqtgen1::MyObjectCxxQtSignalHandlermappedPropertyChanged closure, ::Qt::ConnectionType type);
} // namespace rust::cxxqtgen1
"#}
);
assert_str_eq!(
source,
indoc! {r#"
// Define namespace otherwise we hit a GCC bug
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480
namespace rust::cxxqtlib1 {
template <>
SignalHandler<::rust::cxxqtgen1::MyObjectCxxQtSignalParamsmappedPropertyChanged *>::~SignalHandler()
{
if (data[0] == nullptr && data[1] == nullptr)
{
return;
}
drop_MyObject_signal_handler_mappedPropertyChanged(::std::move(*this));
}
template <>
template <>
void SignalHandler<::rust::cxxqtgen1::MyObjectCxxQtSignalParamsmappedPropertyChanged *>::operator()<MyObject&>(MyObject& self)
{
call_MyObject_signal_handler_mappedPropertyChanged(*this, self);
}
static_assert(alignof(SignalHandler<::rust::cxxqtgen1::MyObjectCxxQtSignalParamsmappedPropertyChanged *>) <= alignof(::std::size_t), "unexpected aligment");
static_assert(sizeof(SignalHandler<::rust::cxxqtgen1::MyObjectCxxQtSignalParamsmappedPropertyChanged *>) == sizeof(::std::size_t[2]), "unexpected size");
} // namespace rust::cxxqtlib1
namespace rust::cxxqtgen1 {
::QMetaObject::Connection
MyObject::mappedPropertyChangedConnect(::rust::Fn<void(MyObject&)> func, ::Qt::ConnectionType type)
MyObject_mappedPropertyChangedConnect(MyObject& self, ::rust::cxxqtgen1::MyObjectCxxQtSignalHandlermappedPropertyChanged closure, ::Qt::ConnectionType type)
{
return ::QObject::connect(this,
return ::QObject::connect(
&self,
&MyObject::mappedPropertyChanged,
this,
[&, func = ::std::move(func)]() {
const ::rust::cxxqtlib1::MaybeLockGuard<MyObject> guard(*this);
func(*this);
&self,
[&, closure = ::std::move(closure)]() mutable {
const ::rust::cxxqtlib1::MaybeLockGuard<MyObject> guard(self);
closure.template operator()<MyObject&>(self);
},
type);
}
} // namespace rust::cxxqtgen1
"#}
);

Expand Down
24 changes: 18 additions & 6 deletions crates/cxx-qt-gen/src/generator/cpp/qobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,40 @@ use syn::Result;

#[derive(Default)]
pub struct GeneratedCppQObjectBlocks {
/// List of includes
pub includes: BTreeSet<String>,
/// List of forward declares before the class and include of the generated CXX header
///
/// For now these are not namespaced
pub forward_declares: Vec<String>,
/// List of forward declares before the class and include of the generated CXX header
//
// TODO: later combine these into forward_declares
// once we have solved how to handle namespacing
pub forward_declares_namespaced: Vec<String>,
/// List of fragments which are outside of the QObject namespace
pub fragments: Vec<CppFragment>,
/// Base class of the QObject
pub base_classes: Vec<String>,
/// List of Qt Meta Object items (eg Q_PROPERTY)
pub metaobjects: Vec<String>,
/// List of public methods for the QObject
pub methods: Vec<CppFragment>,
/// List of private methods for the QObject
pub private_methods: Vec<CppFragment>,
/// List of includes
pub includes: BTreeSet<String>,
/// Base class of the QObject
pub base_classes: Vec<String>,
}

impl GeneratedCppQObjectBlocks {
pub fn append(&mut self, other: &mut Self) {
self.includes.append(&mut other.includes);
self.forward_declares.append(&mut other.forward_declares);
self.forward_declares_namespaced
.append(&mut other.forward_declares_namespaced);
self.fragments.append(&mut other.fragments);
self.base_classes.append(&mut other.base_classes);
self.metaobjects.append(&mut other.metaobjects);
self.methods.append(&mut other.methods);
self.private_methods.append(&mut other.private_methods);
self.includes.append(&mut other.includes);
self.base_classes.append(&mut other.base_classes);
}

pub fn from(qobject: &ParsedQObject) -> GeneratedCppQObjectBlocks {
Expand Down
Loading

0 comments on commit c3a0272

Please sign in to comment.