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

cxx-qt-gen: initial preparation for closures in signals #690

Merged
merged 9 commits into from
Nov 15, 2023
54 changes: 54 additions & 0 deletions crates/cxx-qt-gen/include/cxxqt_signalhandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// clang-format off
// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
// clang-format on
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
// SPDX-FileContributor: Leon Matthes <[email protected]>
//
// SPDX-License-Identifier: MIT OR Apache-2.0
#pragma once

#include <type_traits>

#include "rust/cxx.h"

namespace rust::cxxqtlib1 {

// This represents a Rust Box<dyn FnMut>
//
// It defers all operations to Rust apart for moving, which like Rust is
// performed by a raw memory copy.
template<typename CXXArguments>
class SignalHandler
ahayzen-kdab marked this conversation as resolved.
Show resolved Hide resolved
{

public:
SignalHandler() = delete;
SignalHandler(const SignalHandler&) = delete;

SignalHandler(SignalHandler&& other)
{
data[0] = std::exchange(other.data[0], nullptr);
data[1] = std::exchange(other.data[1], nullptr);
}

~SignalHandler() noexcept;
template<typename... Arguments>
void operator()(Arguments... args);

private:
void* data[2];
};

} // rust::cxxqtlib1

// Define namespace otherwise we hit a GCC bug
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480
namespace rust {

template<typename CXXArguments>
struct IsRelocatable<rust::cxxqtlib1::SignalHandler<CXXArguments>>
: ::std::true_type
{
};

} // namespace rust
20 changes: 5 additions & 15 deletions crates/cxx-qt-gen/src/generator/cpp/externcxxqt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@
// SPDX-License-Identifier: MIT OR Apache-2.0

use crate::{
generator::{
cpp::signal::generate_cpp_free_signal,
naming::namespace::namespace_externcxxqt_with_qobject_namespace,
},
generator::cpp::signal::generate_cpp_free_signal,
parser::{externcxxqt::ParsedExternCxxQt, mappings::ParsedCxxMappings},
CppFragment,
};
use std::collections::BTreeSet;
use syn::Result;

#[derive(Default)]
pub struct GeneratedCppExternCxxQtBlocks {
/// List of includes
pub includes: BTreeSet<String>,
/// List of methods
pub method: CppFragment,
/// Namespace of the method block
Expand All @@ -29,17 +29,7 @@ pub fn generate(

for block in blocks {
for signal in &block.signals {
// Build a namespace that includes any namespace for the T
let namespace = namespace_externcxxqt_with_qobject_namespace(
cxx_mappings
.namespaces
.get(&signal.qobject_ident.to_string()),
);

out.push(GeneratedCppExternCxxQtBlocks {
method: generate_cpp_free_signal(signal, cxx_mappings)?,
namespace,
});
out.push(generate_cpp_free_signal(signal, cxx_mappings)?);
}
}

Expand Down
71 changes: 48 additions & 23 deletions crates/cxx-qt-gen/src/generator/cpp/signal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@

use crate::{
generator::{
cpp::{fragment::CppFragment, qobject::GeneratedCppQObjectBlocks},
cpp::{
externcxxqt::GeneratedCppExternCxxQtBlocks, fragment::CppFragment,
qobject::GeneratedCppQObjectBlocks,
},
naming::namespace::namespace_externcxxqt_with_qobject_namespace,
naming::{qobject::QObjectName, signals::QSignalName},
utils::cpp::syn_type_to_cpp_type,
},
Expand Down Expand Up @@ -64,7 +68,9 @@ fn parameter_types_and_values(
pub fn generate_cpp_free_signal(
signal: &ParsedSignal,
cxx_mappings: &ParsedCxxMappings,
) -> Result<CppFragment> {
) -> Result<GeneratedCppExternCxxQtBlocks> {
let mut generated = GeneratedCppExternCxxQtBlocks::default();

// Prepare the idents we need
let qobject_ident = signal.qobject_ident.to_string();
let qobject_ident_namespaced = cxx_mappings.cxx(&qobject_ident);
Expand All @@ -87,31 +93,44 @@ pub fn generate_cpp_free_signal(
let parameters_types_signal = parameters.types_signal;
let parameters_values_closure = parameters.values_closure;

Ok(CppFragment::Pair {
generated.method = CppFragment::Pair {
header: formatdoc!(
r#"
::QMetaObject::Connection
{qobject_ident}_{connect_ident}({qobject_ident_namespaced}& self, ::rust::Fn<void({parameters_types_closure})> func, ::Qt::ConnectionType type);
"#,
::QMetaObject::Connection
{qobject_ident}_{connect_ident}({qobject_ident_namespaced}& self, ::rust::Fn<void({parameters_types_closure})> func, ::Qt::ConnectionType type);
"#,
),
source: formatdoc! {
r#"
::QMetaObject::Connection
{qobject_ident}_{connect_ident}({qobject_ident_namespaced}& self, ::rust::Fn<void({parameters_types_closure})> func, ::Qt::ConnectionType type)
{{
return ::QObject::connect(
&self,
&{qobject_ident_namespaced}::{signal_ident},
&self,
[&, func = ::std::move(func)]({parameters_types_signal}) {{
const ::rust::cxxqtlib1::MaybeLockGuard<{qobject_ident_namespaced}> guard(self);
func({parameters_values_closure});
}},
type);
}}
"#,
::QMetaObject::Connection
{qobject_ident}_{connect_ident}({qobject_ident_namespaced}& self, ::rust::Fn<void({parameters_types_closure})> func, ::Qt::ConnectionType type)
{{
return ::QObject::connect(
&self,
&{qobject_ident_namespaced}::{signal_ident},
&self,
[&, func = ::std::move(func)]({parameters_types_signal}) {{
const ::rust::cxxqtlib1::MaybeLockGuard<{qobject_ident_namespaced}> guard(self);
func({parameters_values_closure});
}},
type);
}}
"#,
},
})
};

// Build a namespace that includes any namespace for the T
generated.namespace = namespace_externcxxqt_with_qobject_namespace(
cxx_mappings
.namespaces
.get(&signal.qobject_ident.to_string()),
);

generated
.includes
.insert("#include <cxx-qt-common/cxxqt_signalhandler.h>".to_owned());

Ok(generated)
}

pub fn generate_cpp_signals(
Expand All @@ -122,6 +141,12 @@ pub fn generate_cpp_signals(
let mut generated = GeneratedCppQObjectBlocks::default();
let qobject_ident = qobject_idents.cpp_class.cpp.to_string();

if !signals.is_empty() {
generated
.includes
.insert("#include <cxx-qt-common/cxxqt_signalhandler.h>".to_owned());
}

for signal in signals {
// Prepare the idents
let idents = QSignalName::from(signal);
Expand Down Expand Up @@ -390,7 +415,7 @@ mod tests {

let generated = generate_cpp_free_signal(&signal, &ParsedCxxMappings::default()).unwrap();

let (header, source) = if let CppFragment::Pair { header, source } = &generated {
let (header, source) = if let CppFragment::Pair { header, source } = &generated.method {
(header, source)
} else {
panic!("Expected Pair")
Expand Down Expand Up @@ -453,7 +478,7 @@ mod tests {

let generated = generate_cpp_free_signal(&signal, &cxx_mappings).unwrap();

let (header, source) = if let CppFragment::Pair { header, source } = &generated {
let (header, source) = if let CppFragment::Pair { header, source } = &generated.method {
(header, source)
} else {
panic!("Expected Pair")
Expand Down
2 changes: 1 addition & 1 deletion crates/cxx-qt-gen/src/generator/rust/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ impl GeneratedRustBlocks {
.map(|qobject| {
GeneratedRustQObject::from(
qobject,
&parser.cxx_qt_data.cxx_mappings.qualified,
&parser.cxx_qt_data.cxx_mappings,
&parser.passthrough_module.ident,
)
})
Expand Down
32 changes: 19 additions & 13 deletions crates/cxx-qt-gen/src/generator/rust/property/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,16 @@ use crate::{
naming::{property::QPropertyName, qobject::QObjectName},
rust::qobject::GeneratedRustQObject,
},
parser::property::ParsedQProperty,
parser::{mappings::ParsedCxxMappings, property::ParsedQProperty},
};
use std::collections::BTreeMap;
use syn::{Ident, Path, Result};
use syn::Result;

use super::signals::generate_rust_signals;

pub fn generate_rust_properties(
properties: &Vec<ParsedQProperty>,
qobject_idents: &QObjectName,
qualified_mappings: &BTreeMap<Ident, Path>,
cxx_mappings: &ParsedCxxMappings,
) -> Result<GeneratedRustQObject> {
let mut generated = GeneratedRustQObject::default();
let mut signals = vec![];
Expand All @@ -31,7 +30,12 @@ pub fn generate_rust_properties(
let idents = QPropertyName::from(property);

// Getters
let getter = getter::generate(&idents, qobject_idents, &property.ty, qualified_mappings);
let getter = getter::generate(
&idents,
qobject_idents,
&property.ty,
&cxx_mappings.qualified,
);
generated
.cxx_mod_contents
.append(&mut getter.cxx_bridge_as_items()?);
Expand All @@ -40,7 +44,12 @@ pub fn generate_rust_properties(
.append(&mut getter.implementation_as_items()?);

// Setters
let setter = setter::generate(&idents, qobject_idents, &property.ty, qualified_mappings);
let setter = setter::generate(
&idents,
qobject_idents,
&property.ty,
&cxx_mappings.qualified,
);
generated
.cxx_mod_contents
.append(&mut setter.cxx_bridge_as_items()?);
Expand All @@ -55,7 +64,7 @@ pub fn generate_rust_properties(
generated.append(&mut generate_rust_signals(
&signals,
qobject_idents,
qualified_mappings,
cxx_mappings,
)?);

Ok(generated)
Expand Down Expand Up @@ -87,12 +96,9 @@ mod tests {
];
let qobject_idents = create_qobjectname();

let generated = generate_rust_properties(
&properties,
&qobject_idents,
&BTreeMap::<Ident, Path>::default(),
)
.unwrap();
let generated =
generate_rust_properties(&properties, &qobject_idents, &ParsedCxxMappings::default())
.unwrap();

// Check that we have the expected number of blocks
assert_eq!(generated.cxx_mod_contents.len(), 12);
Expand Down
22 changes: 10 additions & 12 deletions crates/cxx-qt-gen/src/generator/rust/qobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
//
// SPDX-License-Identifier: MIT OR Apache-2.0

use std::collections::BTreeMap;

use crate::{
generator::{
naming::{namespace::NamespaceName, qobject::QObjectName},
Expand All @@ -15,10 +13,10 @@ use crate::{
},
utils::rust::syn_ident_cxx_bridge_to_qualified_impl,
},
parser::qobject::ParsedQObject,
parser::{mappings::ParsedCxxMappings, qobject::ParsedQObject},
};
use quote::quote;
use syn::{Ident, Item, Path, Result};
use syn::{Ident, Item, Result};

use super::qenum;

Expand All @@ -39,7 +37,7 @@ impl GeneratedRustQObject {

pub fn from(
qobject: &ParsedQObject,
qualified_mappings: &BTreeMap<Ident, Path>,
cxx_mappings: &ParsedCxxMappings,
module_ident: &Ident,
) -> Result<GeneratedRustQObject> {
// Create the base object
Expand All @@ -56,7 +54,7 @@ impl GeneratedRustQObject {
generated.append(&mut generate_rust_properties(
&qobject.properties,
&qobject_idents,
qualified_mappings,
cxx_mappings,
)?);
generated.append(&mut generate_rust_methods(
&qobject.methods,
Expand All @@ -69,7 +67,7 @@ impl GeneratedRustQObject {
generated.append(&mut generate_rust_signals(
&qobject.signals,
&qobject_idents,
qualified_mappings,
cxx_mappings,
)?);
generated.append(&mut qenum::generate(&qobject.qenums));

Expand All @@ -95,7 +93,7 @@ impl GeneratedRustQObject {
generated.append(&mut threading::generate(
&qobject_idents,
&namespace_idents,
qualified_mappings,
&cxx_mappings.qualified,
module_ident,
)?);
}
Expand All @@ -107,7 +105,7 @@ impl GeneratedRustQObject {
if qobject.locking {
let qualified_impl = syn_ident_cxx_bridge_to_qualified_impl(
&qobject_idents.cpp_class.rust,
qualified_mappings,
&cxx_mappings.qualified,
);
generated.cxx_qt_mod_contents.push(syn::parse_quote! {
impl cxx_qt::Locking for #qualified_impl {}
Expand All @@ -118,13 +116,13 @@ impl GeneratedRustQObject {
&qobject.constructors,
&qobject_idents,
&namespace_idents,
qualified_mappings,
&cxx_mappings.qualified,
module_ident,
)?);

generated.append(&mut cxxqttype::generate(
&qobject_idents,
qualified_mappings,
&cxx_mappings.qualified,
)?);

Ok(generated)
Expand Down Expand Up @@ -202,7 +200,7 @@ mod tests {

let rust = GeneratedRustQObject::from(
parser.cxx_qt_data.qobjects.values().next().unwrap(),
&BTreeMap::<Ident, Path>::default(),
&ParsedCxxMappings::default(),
&format_ident!("ffi"),
)
.unwrap();
Expand Down
Loading
Loading