Skip to content
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
17 changes: 11 additions & 6 deletions crates/cxx-qt-gen/src/generator/cpp/signal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ fn parameter_types_and_values(
parameters: &[ParsedFunctionParameter],
type_names: &TypeNames,
self_ty: &Name,
mutable: bool,
) -> Result<Parameters> {
let mut parameter_named_types_with_self = vec![];
let mut parameter_types_with_self = vec![];
Expand All @@ -66,10 +67,11 @@ fn parameter_types_and_values(

let parameter_named_types = parameter_named_types_with_self.join(", ");

let is_const = if !mutable { " const" } else { "" };
// Insert the extra argument into the closure
let self_ty = self_ty.cxx_qualified();
parameter_named_types_with_self.insert(0, format!("{self_ty}& self"));
parameter_types_with_self.insert(0, format!("{self_ty}&"));
parameter_named_types_with_self.insert(0, format!("{self_ty}{is_const}& self"));
parameter_types_with_self.insert(0, format!("{self_ty}{is_const}&"));
parameter_values_with_self.insert(0, "self".to_owned());

Ok(Parameters {
Expand Down Expand Up @@ -109,7 +111,8 @@ pub fn generate_cpp_signal(
let free_connect_ident_cpp = idents_helper.connect_name.cxx_unqualified();

// Retrieve the parameters for the signal
let parameters = parameter_types_and_values(&signal.parameters, type_names, qobject_name)?;
let parameters =
parameter_types_and_values(&signal.parameters, type_names, qobject_name, signal.mutable)?;
let parameters_named_types = parameters.named_types;
let parameters_named_types_with_self = parameters.named_types_with_self;
let parameter_types_with_self = parameters.types_with_self;
Expand All @@ -121,6 +124,8 @@ pub fn generate_cpp_signal(
let signal_handler_call = idents_helper.function_call;
let signal_handler_drop = idents_helper.function_drop;
let namespace = idents_helper.namespace;
let reference_type = if !signal.mutable { " const &" } else { "&" };
let is_const = if !signal.mutable { " const" } else { "" };

let signal_handler_type = format!("SignalHandler<::{namespace}::{param_struct} *>");

Expand All @@ -135,7 +140,7 @@ pub fn generate_cpp_signal(
// Generate the Q_SIGNAL if this is not an existing signal
if !signal.inherit {
generated.methods.push(CppFragment::Header(format!(
"Q_SIGNAL void {signal_ident}({parameters_named_types});"
"Q_SIGNAL void {signal_ident}({parameters_named_types}){is_const};"
)));
}

Expand All @@ -144,7 +149,7 @@ pub fn generate_cpp_signal(
r#"
namespace {namespace} {{
::QMetaObject::Connection
{free_connect_ident_cpp}({qobject_ident_namespaced}& self, {signal_handler_alias_namespaced} closure, ::Qt::ConnectionType type);
{free_connect_ident_cpp}({qobject_ident_namespaced}{reference_type} self, {signal_handler_alias_namespaced} closure, ::Qt::ConnectionType type);
}} // namespace {namespace}
"#
},
Expand Down Expand Up @@ -177,7 +182,7 @@ pub fn generate_cpp_signal(

namespace {namespace} {{
::QMetaObject::Connection
{free_connect_ident_cpp}({qobject_ident_namespaced}& self, {signal_handler_alias_namespaced} closure, ::Qt::ConnectionType type)
{free_connect_ident_cpp}({qobject_ident_namespaced}{reference_type} self, {signal_handler_alias_namespaced} closure, ::Qt::ConnectionType type)
{{
return ::QObject::connect(
&self,
Expand Down
7 changes: 4 additions & 3 deletions crates/cxx-qt-gen/src/generator/rust/signals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,10 @@ pub fn generate_rust_signal(
let self_type_cxx = if signal.mutable {
parse_quote_spanned! {span => Pin<&mut #qobject_name_rust> }
} else {
// CODECOV_EXCLUDE_START
unreachable!("Signals cannot be immutable right now so this cannot be reached")
// CODECOV_EXCLUDE_STOP
// // CODECOV_EXCLUDE_START
// unreachable!("Signals cannot be immutable right now so this cannot be reached")
// // CODECOV_EXCLUDE_STOP
Comment on lines +88 to +90
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can remove the commented code :-)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

guess we need to ensure there is a unit test that reaches this for coverage though

parse_quote_spanned! {span => &#qobject_name_rust }
};
let self_type_qualified = syn_type_cxx_bridge_to_qualified(&self_type_cxx, type_names)?;
let qualified_impl = qobject_name.rust_qualified();
Expand Down
21 changes: 9 additions & 12 deletions crates/cxx-qt-gen/src/parser/signals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
};
use core::ops::Deref;
use std::ops::DerefMut;
use syn::{spanned::Spanned, Attribute, Error, ForeignItemFn, Result, Visibility};
use syn::{Attribute, ForeignItemFn, Result, Visibility};

#[derive(Clone)]
/// Describes an individual Signal
Expand Down Expand Up @@ -42,12 +42,13 @@ impl ParsedSignal {
let fields = MethodFields::parse(method, auto_case)?;
let attrs = require_attributes(&fields.method.attrs, &Self::ALLOWED_ATTRS)?;

if !fields.mutable {
return Err(Error::new(
fields.method.span(),
"signals must be mutable, use Pin<&mut T> instead of T for the self type",
));
}
// TODO: add proper checks
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, as @LeonMatthesKDAB suggested, we probably only want const signals support inside unsafe extern "C++Qt" blocks not unsafe extern "RustQt", so we need to determine that info somehow here 🤔

// if !fields.mutable {
// return Err(Error::new(
// fields.method.span(),
// "signals must be mutable, use Pin<&mut T> instead of T for the self type",
// ));
// }

let inherit = attrs.contains_key("inherit");

Expand Down Expand Up @@ -96,17 +97,13 @@ mod tests {
assert_parse_errors! {
|input| ParsedSignal::parse(input, CaseConversion::none()) =>

// No immutable signals
{ fn ready(self: &MyObject); }
// No namespaces
{
// No namespaces
#[namespace = "disallowed_namespace"]
fn ready(self: Pin<&mut MyObject>);
}
// Missing self
{ fn ready(x: f64); }
// Self needs to be receiver like self: &T instead of &self
{ fn ready(&self); }
}
}

Expand Down
3 changes: 3 additions & 0 deletions crates/cxx-qt-gen/test_inputs/signals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ mod ffi {
#[qsignal]
fn ready(self: Pin<&mut Self>);

#[qsignal]
fn const_ready(&self);
Comment on lines +29 to +30
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this would need to move up into the C++Qt block to be on the QTimer


#[qsignal]
fn data_changed(
self: Pin<&mut Self>,
Expand Down
55 changes: 55 additions & 0 deletions crates/cxx-qt-gen/test_outputs/signals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,61 @@ MyObject_readyConnect(
}
} // namespace cxx_qt::my_object::rust::cxxqtgen1

// Define namespace otherwise we hit a GCC bug
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480
namespace rust::cxxqt1 {
template<>
SignalHandler<
::cxx_qt::my_object::rust::cxxqtgen1::MyObjectCxxQtSignalParamsconst_ready*>::
~SignalHandler() noexcept
{
if (data[0] == nullptr && data[1] == nullptr) {
return;
}

drop_MyObject_signal_handler_const_ready(::std::move(*this));
}

template<>
template<>
void
SignalHandler<
::cxx_qt::my_object::rust::cxxqtgen1::MyObjectCxxQtSignalParamsconst_ready*>::
operator()<cxx_qt::my_object::MyObject const&>(
cxx_qt::my_object::MyObject const& self)
{
call_MyObject_signal_handler_const_ready(*this, self);
}

static_assert(alignof(SignalHandler<::cxx_qt::my_object::rust::cxxqtgen1::
MyObjectCxxQtSignalParamsconst_ready*>) <=
alignof(::std::size_t),
"unexpected aligment");
static_assert(sizeof(SignalHandler<::cxx_qt::my_object::rust::cxxqtgen1::
MyObjectCxxQtSignalParamsconst_ready*>) ==
sizeof(::std::size_t[2]),
"unexpected size");
} // namespace rust::cxxqt1

namespace cxx_qt::my_object::rust::cxxqtgen1 {
::QMetaObject::Connection
MyObject_const_readyConnect(
cxx_qt::my_object::MyObject const& self,
::cxx_qt::my_object::rust::cxxqtgen1::MyObjectCxxQtSignalHandlerconst_ready
closure,
::Qt::ConnectionType type)
{
return ::QObject::connect(
&self,
&cxx_qt::my_object::MyObject::const_ready,
&self,
[&, closure = ::std::move(closure)]() mutable {
closure.template operator()<cxx_qt::my_object::MyObject const&>(self);
},
type);
}
} // namespace cxx_qt::my_object::rust::cxxqtgen1

// Define namespace otherwise we hit a GCC bug
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480
namespace rust::cxxqt1 {
Expand Down
15 changes: 15 additions & 0 deletions crates/cxx-qt-gen/test_outputs/signals.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ using MyObjectCxxQtSignalHandlerready =
::rust::cxxqt1::SignalHandler<struct MyObjectCxxQtSignalParamsready*>;
} // namespace cxx_qt::my_object::rust::cxxqtgen1

namespace cxx_qt::my_object::rust::cxxqtgen1 {
using MyObjectCxxQtSignalHandlerconst_ready =
::rust::cxxqt1::SignalHandler<struct MyObjectCxxQtSignalParamsconst_ready*>;
} // namespace cxx_qt::my_object::rust::cxxqtgen1

namespace cxx_qt::my_object::rust::cxxqtgen1 {
using MyObjectCxxQtSignalHandlerdata_changed =
::rust::cxxqt1::SignalHandler<struct MyObjectCxxQtSignalParamsdata_changed*>;
Expand Down Expand Up @@ -47,6 +52,15 @@ MyObject_readyConnect(
::Qt::ConnectionType type);
} // namespace cxx_qt::my_object::rust::cxxqtgen1

namespace cxx_qt::my_object::rust::cxxqtgen1 {
::QMetaObject::Connection
MyObject_const_readyConnect(
cxx_qt::my_object::MyObject const& self,
::cxx_qt::my_object::rust::cxxqtgen1::MyObjectCxxQtSignalHandlerconst_ready
closure,
::Qt::ConnectionType type);
} // namespace cxx_qt::my_object::rust::cxxqtgen1

namespace cxx_qt::my_object::rust::cxxqtgen1 {
::QMetaObject::Connection
MyObject_data_changedConnect(
Expand Down Expand Up @@ -77,6 +91,7 @@ class MyObject
public:
Q_INVOKABLE void invokable() noexcept;
Q_SIGNAL void ready();
Q_SIGNAL void const_ready() const;
Q_SIGNAL void data_changed(::std::int32_t first,
::std::unique_ptr<Opaque> second,
QPoint third,
Expand Down
91 changes: 91 additions & 0 deletions crates/cxx-qt-gen/test_outputs/signals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,37 @@ mod ffi {
self_value: Pin<&mut MyObject>,
);
}
unsafe extern "C++" {
#[cxx_name = "const_ready"]
#[namespace = "cxx_qt::my_object"]
fn const_ready(self: &MyObject);
}
unsafe extern "C++" {
#[doc(hidden)]
#[namespace = "cxx_qt::my_object::rust::cxxqtgen1"]
type MyObjectCxxQtSignalHandlerconst_ready<'a> = cxx_qt::signalhandler::CxxQtSignalHandler<
'a,
super::MyObjectCxxQtSignalClosureconst_ready,
>;
#[doc(hidden)]
#[namespace = "cxx_qt::my_object::rust::cxxqtgen1"]
#[cxx_name = "MyObject_const_readyConnect"]
fn MyObject_connect_const_ready(
self_value: &MyObject,
signal_handler: MyObjectCxxQtSignalHandlerconst_ready,
conn_type: CxxQtConnectionType,
) -> CxxQtQMetaObjectConnection;
}
#[namespace = "cxx_qt::my_object::rust::cxxqtgen1"]
extern "Rust" {
#[doc(hidden)]
fn drop_MyObject_signal_handler_const_ready(handler: MyObjectCxxQtSignalHandlerconst_ready);
#[doc(hidden)]
fn call_MyObject_signal_handler_const_ready(
handler: &mut MyObjectCxxQtSignalHandlerconst_ready,
self_value: &MyObject,
);
}
unsafe extern "C++" {
#[cxx_name = "data_changed"]
#[namespace = "cxx_qt::my_object"]
Expand Down Expand Up @@ -289,6 +320,66 @@ cxx_qt::static_assertions::assert_eq_size!(
cxx_qt::signalhandler::CxxQtSignalHandler<MyObjectCxxQtSignalClosureready>,
[usize; 2]
);
impl ffi::MyObject {
#[doc = "Connect the given function pointer to the signal "]
#[doc = "const_ready"]
#[doc = ", so that when the signal is emitted the function pointer is executed."]
pub fn connect_const_ready<'a, F: FnMut(&ffi::MyObject) + 'a + Send>(
self: &ffi::MyObject,
closure: F,
conn_type: cxx_qt::ConnectionType,
) -> cxx_qt::QScopedMetaObjectConnectionGuard<'a> {
cxx_qt::QScopedMetaObjectConnectionGuard::from(ffi::MyObject_connect_const_ready(
self,
cxx_qt::signalhandler::CxxQtSignalHandler::<MyObjectCxxQtSignalClosureconst_ready>::new(
Box::new(closure),
),
conn_type,
))
}
}
impl ffi::MyObject {
#[doc = "Connect the given function pointer to the signal "]
#[doc = "const_ready"]
#[doc = ", so that when the signal is emitted the function pointer is executed."]
#[doc = "\n"]
#[doc = "Note that this method uses a AutoConnection connection type."]
pub fn on_const_ready<'a, F: FnMut(&ffi::MyObject) + 'a + Send>(
self: &ffi::MyObject,
closure: F,
) -> cxx_qt::QScopedMetaObjectConnectionGuard<'a> {
cxx_qt::QScopedMetaObjectConnectionGuard::from(ffi::MyObject_connect_const_ready(
self,
cxx_qt::signalhandler::CxxQtSignalHandler::<MyObjectCxxQtSignalClosureconst_ready>::new(
Box::new(closure),
),
cxx_qt::ConnectionType::AutoConnection,
))
}
}
#[doc(hidden)]
pub struct MyObjectCxxQtSignalClosureconst_ready {}
impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure for MyObjectCxxQtSignalClosureconst_ready {
type Id = cxx::type_id!(
"::cxx_qt::my_object::rust::cxxqtgen1::MyObjectCxxQtSignalHandlerconst_ready"
);
type FnType<'a> = dyn FnMut(&ffi::MyObject) + 'a + Send;
}
use core::mem::drop as drop_MyObject_signal_handler_const_ready;
fn call_MyObject_signal_handler_const_ready(
handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler<MyObjectCxxQtSignalClosureconst_ready>,
self_value: &ffi::MyObject,
) {
handler.closure()(self_value);
}
cxx_qt::static_assertions::assert_eq_align!(
cxx_qt::signalhandler::CxxQtSignalHandler<MyObjectCxxQtSignalClosureconst_ready>,
usize
);
cxx_qt::static_assertions::assert_eq_size!(
cxx_qt::signalhandler::CxxQtSignalHandler<MyObjectCxxQtSignalClosureconst_ready>,
[usize; 2]
);
impl ffi::MyObject {
#[doc = "Connect the given function pointer to the signal "]
#[doc = "data_changed"]
Expand Down
1 change: 1 addition & 0 deletions examples/qml_features/cpp/external_qobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ class ExternalQObject : public QObject
Q_SIGNALS:
void triggered();
void triggeredPrivateSignal(QPrivateSignal);
void triggeredConstSignal() const;
};
5 changes: 5 additions & 0 deletions examples/qml_features/rust/src/externcxxqt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ pub mod ffi {
#[qsignal]
fn triggered(self: Pin<&mut Self>);

/// const signal that is emitted when trigger is fired
#[qsignal]
#[rust_name = "triggered_const_signal"]
fn triggeredConstSignal(&self);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


/// Private signal that is emitted when trigger is fired
#[qsignal]
#[rust_name = "triggered_private_signal"]
Expand Down
Loading