Skip to content

Commit c25611d

Browse files
Allow immutable signals, and add one to qml_features
1 parent c6710b7 commit c25611d

File tree

9 files changed

+194
-21
lines changed

9 files changed

+194
-21
lines changed

crates/cxx-qt-gen/src/generator/cpp/signal.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ fn parameter_types_and_values(
5151
parameters: &[ParsedFunctionParameter],
5252
type_names: &TypeNames,
5353
self_ty: &Name,
54+
mutable: bool,
5455
) -> Result<Parameters> {
5556
let mut parameter_named_types_with_self = vec![];
5657
let mut parameter_types_with_self = vec![];
@@ -66,10 +67,11 @@ fn parameter_types_and_values(
6667

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

70+
let is_const = if !mutable { " const" } else { "" };
6971
// Insert the extra argument into the closure
7072
let self_ty = self_ty.cxx_qualified();
71-
parameter_named_types_with_self.insert(0, format!("{self_ty}& self"));
72-
parameter_types_with_self.insert(0, format!("{self_ty}&"));
73+
parameter_named_types_with_self.insert(0, format!("{self_ty}{is_const}& self"));
74+
parameter_types_with_self.insert(0, format!("{self_ty}{is_const}&"));
7375
parameter_values_with_self.insert(0, "self".to_owned());
7476

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

111113
// Retrieve the parameters for the signal
112-
let parameters = parameter_types_and_values(&signal.parameters, type_names, qobject_name)?;
114+
let parameters =
115+
parameter_types_and_values(&signal.parameters, type_names, qobject_name, signal.mutable)?;
113116
let parameters_named_types = parameters.named_types;
114117
let parameters_named_types_with_self = parameters.named_types_with_self;
115118
let parameter_types_with_self = parameters.types_with_self;
@@ -121,6 +124,8 @@ pub fn generate_cpp_signal(
121124
let signal_handler_call = idents_helper.function_call;
122125
let signal_handler_drop = idents_helper.function_drop;
123126
let namespace = idents_helper.namespace;
127+
let reference_type = if !signal.mutable { " const &" } else { "&" };
128+
let is_const = if !signal.mutable { " const" } else { "" };
124129

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

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

@@ -144,7 +149,7 @@ pub fn generate_cpp_signal(
144149
r#"
145150
namespace {namespace} {{
146151
::QMetaObject::Connection
147-
{free_connect_ident_cpp}({qobject_ident_namespaced}& self, {signal_handler_alias_namespaced} closure, ::Qt::ConnectionType type);
152+
{free_connect_ident_cpp}({qobject_ident_namespaced}{reference_type} self, {signal_handler_alias_namespaced} closure, ::Qt::ConnectionType type);
148153
}} // namespace {namespace}
149154
"#
150155
},
@@ -177,7 +182,7 @@ pub fn generate_cpp_signal(
177182
178183
namespace {namespace} {{
179184
::QMetaObject::Connection
180-
{free_connect_ident_cpp}({qobject_ident_namespaced}& self, {signal_handler_alias_namespaced} closure, ::Qt::ConnectionType type)
185+
{free_connect_ident_cpp}({qobject_ident_namespaced}{reference_type} self, {signal_handler_alias_namespaced} closure, ::Qt::ConnectionType type)
181186
{{
182187
return ::QObject::connect(
183188
&self,

crates/cxx-qt-gen/src/generator/rust/signals.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,10 @@ pub fn generate_rust_signal(
8585
let self_type_cxx = if signal.mutable {
8686
parse_quote_spanned! {span => Pin<&mut #qobject_name_rust> }
8787
} else {
88-
// CODECOV_EXCLUDE_START
89-
unreachable!("Signals cannot be immutable right now so this cannot be reached")
90-
// CODECOV_EXCLUDE_STOP
88+
// // CODECOV_EXCLUDE_START
89+
// unreachable!("Signals cannot be immutable right now so this cannot be reached")
90+
// // CODECOV_EXCLUDE_STOP
91+
parse_quote_spanned! {span => &#qobject_name_rust }
9192
};
9293
let self_type_qualified = syn_type_cxx_bridge_to_qualified(&self_type_cxx, type_names)?;
9394
let qualified_impl = qobject_name.rust_qualified();

crates/cxx-qt-gen/src/parser/signals.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
};
1010
use core::ops::Deref;
1111
use std::ops::DerefMut;
12-
use syn::{spanned::Spanned, Attribute, Error, ForeignItemFn, Result, Visibility};
12+
use syn::{Attribute, ForeignItemFn, Result, Visibility};
1313

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

45-
if !fields.mutable {
46-
return Err(Error::new(
47-
fields.method.span(),
48-
"signals must be mutable, use Pin<&mut T> instead of T for the self type",
49-
));
50-
}
45+
// TODO: add proper checks
46+
// if !fields.mutable {
47+
// return Err(Error::new(
48+
// fields.method.span(),
49+
// "signals must be mutable, use Pin<&mut T> instead of T for the self type",
50+
// ));
51+
// }
5152

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

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

99-
// No immutable signals
100-
{ fn ready(self: &MyObject); }
100+
// No namespaces
101101
{
102-
// No namespaces
103102
#[namespace = "disallowed_namespace"]
104103
fn ready(self: Pin<&mut MyObject>);
105104
}
106105
// Missing self
107106
{ fn ready(x: f64); }
108-
// Self needs to be receiver like self: &T instead of &self
109-
{ fn ready(&self); }
110107
}
111108
}
112109

crates/cxx-qt-gen/test_inputs/signals.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ mod ffi {
2626
#[qsignal]
2727
fn ready(self: Pin<&mut Self>);
2828

29+
#[qsignal]
30+
fn const_ready(&self);
31+
2932
#[qsignal]
3033
fn data_changed(
3134
self: Pin<&mut Self>,

crates/cxx-qt-gen/test_outputs/signals.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,61 @@ MyObject_readyConnect(
112112
}
113113
} // namespace cxx_qt::my_object::rust::cxxqtgen1
114114

115+
// Define namespace otherwise we hit a GCC bug
116+
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480
117+
namespace rust::cxxqt1 {
118+
template<>
119+
SignalHandler<
120+
::cxx_qt::my_object::rust::cxxqtgen1::MyObjectCxxQtSignalParamsconst_ready*>::
121+
~SignalHandler() noexcept
122+
{
123+
if (data[0] == nullptr && data[1] == nullptr) {
124+
return;
125+
}
126+
127+
drop_MyObject_signal_handler_const_ready(::std::move(*this));
128+
}
129+
130+
template<>
131+
template<>
132+
void
133+
SignalHandler<
134+
::cxx_qt::my_object::rust::cxxqtgen1::MyObjectCxxQtSignalParamsconst_ready*>::
135+
operator()<cxx_qt::my_object::MyObject const&>(
136+
cxx_qt::my_object::MyObject const& self)
137+
{
138+
call_MyObject_signal_handler_const_ready(*this, self);
139+
}
140+
141+
static_assert(alignof(SignalHandler<::cxx_qt::my_object::rust::cxxqtgen1::
142+
MyObjectCxxQtSignalParamsconst_ready*>) <=
143+
alignof(::std::size_t),
144+
"unexpected aligment");
145+
static_assert(sizeof(SignalHandler<::cxx_qt::my_object::rust::cxxqtgen1::
146+
MyObjectCxxQtSignalParamsconst_ready*>) ==
147+
sizeof(::std::size_t[2]),
148+
"unexpected size");
149+
} // namespace rust::cxxqt1
150+
151+
namespace cxx_qt::my_object::rust::cxxqtgen1 {
152+
::QMetaObject::Connection
153+
MyObject_const_readyConnect(
154+
cxx_qt::my_object::MyObject const& self,
155+
::cxx_qt::my_object::rust::cxxqtgen1::MyObjectCxxQtSignalHandlerconst_ready
156+
closure,
157+
::Qt::ConnectionType type)
158+
{
159+
return ::QObject::connect(
160+
&self,
161+
&cxx_qt::my_object::MyObject::const_ready,
162+
&self,
163+
[&, closure = ::std::move(closure)]() mutable {
164+
closure.template operator()<cxx_qt::my_object::MyObject const&>(self);
165+
},
166+
type);
167+
}
168+
} // namespace cxx_qt::my_object::rust::cxxqtgen1
169+
115170
// Define namespace otherwise we hit a GCC bug
116171
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480
117172
namespace rust::cxxqt1 {

crates/cxx-qt-gen/test_outputs/signals.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ using MyObjectCxxQtSignalHandlerready =
1414
::rust::cxxqt1::SignalHandler<struct MyObjectCxxQtSignalParamsready*>;
1515
} // namespace cxx_qt::my_object::rust::cxxqtgen1
1616

17+
namespace cxx_qt::my_object::rust::cxxqtgen1 {
18+
using MyObjectCxxQtSignalHandlerconst_ready =
19+
::rust::cxxqt1::SignalHandler<struct MyObjectCxxQtSignalParamsconst_ready*>;
20+
} // namespace cxx_qt::my_object::rust::cxxqtgen1
21+
1722
namespace cxx_qt::my_object::rust::cxxqtgen1 {
1823
using MyObjectCxxQtSignalHandlerdata_changed =
1924
::rust::cxxqt1::SignalHandler<struct MyObjectCxxQtSignalParamsdata_changed*>;
@@ -47,6 +52,15 @@ MyObject_readyConnect(
4752
::Qt::ConnectionType type);
4853
} // namespace cxx_qt::my_object::rust::cxxqtgen1
4954

55+
namespace cxx_qt::my_object::rust::cxxqtgen1 {
56+
::QMetaObject::Connection
57+
MyObject_const_readyConnect(
58+
cxx_qt::my_object::MyObject const& self,
59+
::cxx_qt::my_object::rust::cxxqtgen1::MyObjectCxxQtSignalHandlerconst_ready
60+
closure,
61+
::Qt::ConnectionType type);
62+
} // namespace cxx_qt::my_object::rust::cxxqtgen1
63+
5064
namespace cxx_qt::my_object::rust::cxxqtgen1 {
5165
::QMetaObject::Connection
5266
MyObject_data_changedConnect(
@@ -77,6 +91,7 @@ class MyObject
7791
public:
7892
Q_INVOKABLE void invokable() noexcept;
7993
Q_SIGNAL void ready();
94+
Q_SIGNAL void const_ready() const;
8095
Q_SIGNAL void data_changed(::std::int32_t first,
8196
::std::unique_ptr<Opaque> second,
8297
QPoint third,

crates/cxx-qt-gen/test_outputs/signals.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,37 @@ mod ffi {
7373
self_value: Pin<&mut MyObject>,
7474
);
7575
}
76+
unsafe extern "C++" {
77+
#[cxx_name = "const_ready"]
78+
#[namespace = "cxx_qt::my_object"]
79+
fn const_ready(self: &MyObject);
80+
}
81+
unsafe extern "C++" {
82+
#[doc(hidden)]
83+
#[namespace = "cxx_qt::my_object::rust::cxxqtgen1"]
84+
type MyObjectCxxQtSignalHandlerconst_ready<'a> = cxx_qt::signalhandler::CxxQtSignalHandler<
85+
'a,
86+
super::MyObjectCxxQtSignalClosureconst_ready,
87+
>;
88+
#[doc(hidden)]
89+
#[namespace = "cxx_qt::my_object::rust::cxxqtgen1"]
90+
#[cxx_name = "MyObject_const_readyConnect"]
91+
fn MyObject_connect_const_ready(
92+
self_value: &MyObject,
93+
signal_handler: MyObjectCxxQtSignalHandlerconst_ready,
94+
conn_type: CxxQtConnectionType,
95+
) -> CxxQtQMetaObjectConnection;
96+
}
97+
#[namespace = "cxx_qt::my_object::rust::cxxqtgen1"]
98+
extern "Rust" {
99+
#[doc(hidden)]
100+
fn drop_MyObject_signal_handler_const_ready(handler: MyObjectCxxQtSignalHandlerconst_ready);
101+
#[doc(hidden)]
102+
fn call_MyObject_signal_handler_const_ready(
103+
handler: &mut MyObjectCxxQtSignalHandlerconst_ready,
104+
self_value: &MyObject,
105+
);
106+
}
76107
unsafe extern "C++" {
77108
#[cxx_name = "data_changed"]
78109
#[namespace = "cxx_qt::my_object"]
@@ -289,6 +320,66 @@ cxx_qt::static_assertions::assert_eq_size!(
289320
cxx_qt::signalhandler::CxxQtSignalHandler<MyObjectCxxQtSignalClosureready>,
290321
[usize; 2]
291322
);
323+
impl ffi::MyObject {
324+
#[doc = "Connect the given function pointer to the signal "]
325+
#[doc = "const_ready"]
326+
#[doc = ", so that when the signal is emitted the function pointer is executed."]
327+
pub fn connect_const_ready<'a, F: FnMut(&ffi::MyObject) + 'a + Send>(
328+
self: &ffi::MyObject,
329+
closure: F,
330+
conn_type: cxx_qt::ConnectionType,
331+
) -> cxx_qt::QScopedMetaObjectConnectionGuard<'a> {
332+
cxx_qt::QScopedMetaObjectConnectionGuard::from(ffi::MyObject_connect_const_ready(
333+
self,
334+
cxx_qt::signalhandler::CxxQtSignalHandler::<MyObjectCxxQtSignalClosureconst_ready>::new(
335+
Box::new(closure),
336+
),
337+
conn_type,
338+
))
339+
}
340+
}
341+
impl ffi::MyObject {
342+
#[doc = "Connect the given function pointer to the signal "]
343+
#[doc = "const_ready"]
344+
#[doc = ", so that when the signal is emitted the function pointer is executed."]
345+
#[doc = "\n"]
346+
#[doc = "Note that this method uses a AutoConnection connection type."]
347+
pub fn on_const_ready<'a, F: FnMut(&ffi::MyObject) + 'a + Send>(
348+
self: &ffi::MyObject,
349+
closure: F,
350+
) -> cxx_qt::QScopedMetaObjectConnectionGuard<'a> {
351+
cxx_qt::QScopedMetaObjectConnectionGuard::from(ffi::MyObject_connect_const_ready(
352+
self,
353+
cxx_qt::signalhandler::CxxQtSignalHandler::<MyObjectCxxQtSignalClosureconst_ready>::new(
354+
Box::new(closure),
355+
),
356+
cxx_qt::ConnectionType::AutoConnection,
357+
))
358+
}
359+
}
360+
#[doc(hidden)]
361+
pub struct MyObjectCxxQtSignalClosureconst_ready {}
362+
impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure for MyObjectCxxQtSignalClosureconst_ready {
363+
type Id = cxx::type_id!(
364+
"::cxx_qt::my_object::rust::cxxqtgen1::MyObjectCxxQtSignalHandlerconst_ready"
365+
);
366+
type FnType<'a> = dyn FnMut(&ffi::MyObject) + 'a + Send;
367+
}
368+
use core::mem::drop as drop_MyObject_signal_handler_const_ready;
369+
fn call_MyObject_signal_handler_const_ready(
370+
handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler<MyObjectCxxQtSignalClosureconst_ready>,
371+
self_value: &ffi::MyObject,
372+
) {
373+
handler.closure()(self_value);
374+
}
375+
cxx_qt::static_assertions::assert_eq_align!(
376+
cxx_qt::signalhandler::CxxQtSignalHandler<MyObjectCxxQtSignalClosureconst_ready>,
377+
usize
378+
);
379+
cxx_qt::static_assertions::assert_eq_size!(
380+
cxx_qt::signalhandler::CxxQtSignalHandler<MyObjectCxxQtSignalClosureconst_ready>,
381+
[usize; 2]
382+
);
292383
impl ffi::MyObject {
293384
#[doc = "Connect the given function pointer to the signal "]
294385
#[doc = "data_changed"]

examples/qml_features/cpp/external_qobject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ class ExternalQObject : public QObject
2222
Q_SIGNALS:
2323
void triggered();
2424
void triggeredPrivateSignal(QPrivateSignal);
25+
void triggeredConstSignal() const;
2526
};

examples/qml_features/rust/src/externcxxqt.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ pub mod ffi {
2222
#[qsignal]
2323
fn triggered(self: Pin<&mut Self>);
2424

25+
/// const signal that is emitted when trigger is fired
26+
#[qsignal]
27+
#[rust_name = "triggered_const_signal"]
28+
fn triggeredConstSignal(&self);
29+
2530
/// Private signal that is emitted when trigger is fired
2631
#[qsignal]
2732
#[rust_name = "triggered_private_signal"]

0 commit comments

Comments
 (0)