Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion book/src/bridge/extern_rustqt.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ is equivalent to writing
impl cxx_qt::Constructor<()> for x {}
```

inside the bridge.
inside the bridge. You can then implement your constructors outside the bridge, using either of these traits.

For further documentation see the [traits page](./traits.md).

Expand Down
2 changes: 1 addition & 1 deletion book/src/bridge/traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ For further documentation, refer to the documentation of the individual traits:

- [CxxQtType](https://docs.rs/cxx-qt/latest/cxx_qt/trait.CxxQtType.html) - trait to reach the Rust implementation of a `QObject`
- This trait is automatically implemented for any `#[qobject]` type inside `extern "RustQt"` blocks.
- [Constructor](https://docs.rs/cxx-qt/latest/cxx_qt/trait.Constructor.html) - custom constructor
- [Constructor](https://docs.rs/cxx-qt/latest/cxx_qt/trait.Constructor.html) - custom constructor. This must be declared in the bridge in order for you to implement it outside the bridge
- [Initialize](https://docs.rs/cxx-qt/latest/cxx_qt/trait.Initialize.html) - execute Rust code when the object is constructed, or as shorthand for an empty constructor
- [Threading](https://docs.rs/cxx-qt/latest/cxx_qt/trait.Threading.html) - marker trait whether CXX-Qt threading should be enabled
- [QObjectExt](https://docs.rs/cxx-qt/latest/cxx_qt_lib/trait.QObjectExt.html) - Trait which exposes some key methods of QObject
Expand Down
40 changes: 34 additions & 6 deletions crates/cxx-qt-gen/src/generator/rust/constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,24 @@ pub fn generate(
let rust_struct_name_rust = qobject_names.rust_struct.rust_unqualified();

for (index, constructor) in constructors.iter().enumerate() {
let mut arguments = TokenStream::new();
for elem in &constructor.arguments {
arguments.extend(quote!(#elem,));
}

let lifetime = constructor.lifetime.as_ref().map(|lifetime| {
quote! {
< #lifetime >
}
});

result
.cxx_qt_mod_contents
.append(&mut vec![parse_quote_spanned! {
constructor.imp.span() =>
impl #lifetime ::cxx_qt::ConstructorDeclared<(#arguments) > for #qobject_name_rust_qualified {}
}]);

let arguments_lifetime =
lifetime_of_arguments(&constructor.lifetime, &constructor.arguments)?;
let base_lifetime =
Expand Down Expand Up @@ -599,8 +612,16 @@ mod tests {
},
);

// Shim impl only appears once
assert_tokens_eq(
&blocks.cxx_qt_mod_contents[0],
quote! {
impl ::cxx_qt::ConstructorDeclared<()> for qobject::MyObject {}
},
);

assert_tokens_eq(
&blocks.cxx_qt_mod_contents[1],
quote! {
#[doc(hidden)]
pub fn route_arguments_MyObject_0() -> qobject::CxxQtConstructorArgumentsMyObject0
Expand All @@ -619,7 +640,7 @@ mod tests {
},
);
assert_tokens_eq(
&blocks.cxx_qt_mod_contents[1],
&blocks.cxx_qt_mod_contents[2],
quote! {
#[doc(hidden)]
#[allow(unused_variables)]
Expand All @@ -633,7 +654,7 @@ mod tests {
},
);
assert_tokens_eq(
&blocks.cxx_qt_mod_contents[2],
&blocks.cxx_qt_mod_contents[3],
quote! {
#[doc(hidden)]
#[allow(unused_variables)]
Expand Down Expand Up @@ -725,7 +746,14 @@ mod tests {
);

assert_tokens_eq(
&blocks.cxx_qt_mod_contents[3],
&blocks.cxx_qt_mod_contents[4],
quote! {
impl<'lifetime> ::cxx_qt::ConstructorDeclared<(*const QObject,)> for qobject::MyObject {}
},
);

assert_tokens_eq(
&blocks.cxx_qt_mod_contents[5],
quote! {
#[doc(hidden)]
pub fn route_arguments_MyObject_1<'lifetime>(arg0: *const QObject) -> qobject::CxxQtConstructorArgumentsMyObject1<'lifetime>
Expand Down Expand Up @@ -753,7 +781,7 @@ mod tests {
},
);
assert_tokens_eq(
&blocks.cxx_qt_mod_contents[4],
&blocks.cxx_qt_mod_contents[6],
quote! {
#[doc(hidden)]
#[allow(unused_variables)]
Expand All @@ -767,7 +795,7 @@ mod tests {
},
);
assert_tokens_eq(
&blocks.cxx_qt_mod_contents[5],
&blocks.cxx_qt_mod_contents[7],
quote! {
#[doc(hidden)]
#[allow(unused_variables)]
Expand Down Expand Up @@ -806,7 +834,7 @@ mod tests {
]);

assert_eq!(blocks.cxx_mod_contents.len(), 10);
assert_eq!(blocks.cxx_qt_mod_contents.len(), 6);
assert_eq!(blocks.cxx_qt_mod_contents.len(), 8);

let namespace_attr = quote! {
#[namespace = "qobject::cxx_qt_MyObject"]
Expand Down
2 changes: 2 additions & 0 deletions crates/cxx-qt-gen/test_outputs/invokables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ unsafe impl ::cxx_qt::casting::Upcast<::cxx_qt::QObject> for ffi::MyObject {
ffi::cxx_qt_ffi_MyObject_downcastPtr(base)
}
}
impl<'a> ::cxx_qt::ConstructorDeclared<(i32, &'a QString)> for ffi::MyObject {}
#[doc(hidden)]
pub fn route_arguments_MyObject_0<'a>(
arg0: i32,
Expand Down Expand Up @@ -376,6 +377,7 @@ pub fn initialize_MyObject_0<'a>(
) {
<ffi::MyObject as cxx_qt::Constructor<(i32, &'a ffi::QString)>>::initialize(qobject, ());
}
impl ::cxx_qt::ConstructorDeclared<()> for ffi::MyObject {}
#[doc(hidden)]
pub fn route_arguments_MyObject_1() -> ffi::CxxQtConstructorArgumentsMyObject1 {
#[allow(unused_variables)]
Expand Down
7 changes: 5 additions & 2 deletions crates/cxx-qt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,9 @@ pub trait Threading: Sized {
fn threading_drop(cxx_qt_thread: core::pin::Pin<&mut CxxQtThread<Self>>);
}

#[doc(hidden)]
Copy link
Collaborator

Choose a reason for hiding this comment

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

I just tried the PR, it works well, but the discoverability of what caused the error still needs improving!

If I e.g. remove the constructor declaration from qml_features/properties.rs, what I get is:

Long error message
error[E0277]: the trait bound `RustProperties: ConstructorDeclared<(bool, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QString)>` is not satisfied
   --> examples/qml_features/rust/src/properties.rs:139:5
    |
139 | /     fn route_arguments(
140 | |         arguments: (bool, &'a QUrl, &'a QUrl, &'a QString),
141 | |     ) -> (
142 | |         Self::NewArguments,
143 | |         Self::BaseArguments,
144 | |         Self::InitializeArguments,
145 | |     ) {
    | |_____^ the trait `ConstructorDeclared<(bool, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QString)>` is not implemented for `RustProperties`
    |
    = help: the trait `ConstructorDeclared<(bool, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QString)>` is not implemented for `RustProperties`
            but trait `ConstructorDeclared<()>` is implemented for it
    = help: for that trait implementation, expected `()`, found `(bool, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QString)`
note: required by a bound in `Constructor`
   --> /home/kdab/Documents/projects/3371-Rust-RnD/cxx-qt/crates/cxx-qt/src/lib.rs:313:47
    |
313 | pub trait Constructor<Arguments>: CxxQtType + ConstructorDeclared<Arguments> {
    |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Constructor`

error[E0277]: the trait bound `RustProperties: ConstructorDeclared<(bool, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QString)>` is not satisfied
   --> examples/qml_features/rust/src/properties.rs:150:5
    |
150 | /     fn new(
151 | |         (connected, connected_url, previous_connected_url, status_message): Self::NewArguments,
152 | |     ) -> Self::Rust {
    | |___________________^ the trait `ConstructorDeclared<(bool, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QString)>` is not implemented for `RustProperties`
    |
    = help: the trait `ConstructorDeclared<(bool, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QString)>` is not implemented for `RustProperties`
            but trait `ConstructorDeclared<()>` is implemented for it
    = help: for that trait implementation, expected `()`, found `(bool, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QString)`
note: required by a bound in `Constructor`
   --> /home/kdab/Documents/projects/3371-Rust-RnD/cxx-qt/crates/cxx-qt/src/lib.rs:313:47
    |
313 | pub trait Constructor<Arguments>: CxxQtType + ConstructorDeclared<Arguments> {
    |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Constructor`

error[E0277]: the trait bound `RustProperties: ConstructorDeclared<(bool, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QString)>` is not satisfied
   --> examples/qml_features/rust/src/properties.rs:134:75
    |
134 | impl<'a> cxx_qt::Constructor<(bool, &'a QUrl, &'a QUrl, &'a QString)> for qobject::RustProperties {
    |                                                                           ^^^^^^^^^^^^^^^^^^^^^^^ the trait `ConstructorDeclared<(bool, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QString)>` is not implemented for `RustProperties`
    |
    = help: the trait `ConstructorDeclared<(bool, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QString)>` is not implemented for `RustProperties`
            but trait `ConstructorDeclared<()>` is implemented for it
    = help: for that trait implementation, expected `()`, found `(bool, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QUrl, &'a cxx_qt_lib::QString)`
note: required by a bound in `Constructor`
   --> /home/kdab/Documents/projects/3371-Rust-RnD/cxx-qt/crates/cxx-qt/src/lib.rs:313:47
    |
313 | pub trait Constructor<Arguments>: CxxQtType + ConstructorDeclared<Arguments> {
    |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Constructor`

So now I know I did something wrong, but what?

The compiler helpfully points me me to the Constructor trait. But if I look at the docs for the constructor trait, I cannot click on ConstructorDeclared, and I cannot find the trait mentioned anywhere in the docs.

So either we:

  • Make this not #[doc(hidden)] and add documentation that says that you should not implement the trait yourself, but that adding the declaration to the bridge generates an implementation.
  • Describe this in the doc comment of the Constructor trait.

pub trait ConstructorDeclared<Arguments> {}

/// This trait can be implemented on any [CxxQtType] to define a
/// custom constructor in C++ for the QObject.
///
Expand Down Expand Up @@ -307,7 +310,7 @@ pub trait Threading: Sized {
///
/// If a QObject implements the `Initialize` trait, and the inner Rust struct is [Default]-constructible it will automatically implement `cxx_qt::Constructor<()>`.
/// Additionally, implementing `impl cxx_qt::Initialize` will act as shorthand for `cxx_qt::Constructor<()>`.
pub trait Constructor<Arguments>: CxxQtType {
pub trait Constructor<Arguments>: CxxQtType + ConstructorDeclared<Arguments> {
/// The arguments that are passed to the [`new()`](Self::new) function to construct the inner Rust struct.
/// This must be a tuple of CXX compatible types.
///
Expand Down Expand Up @@ -394,7 +397,7 @@ pub trait Constructor<Arguments>: CxxQtType {
/// ```
// TODO: Once the QObject type is available in the cxx-qt crate, also auto-generate a default
// constructor that takes QObject and passes it to the parent.
pub trait Initialize: CxxQtType {
pub trait Initialize: CxxQtType + ConstructorDeclared<()> {
/// This function is called to initialize the QObject after construction.
fn initialize(self: core::pin::Pin<&mut Self>);
}
Expand Down
Loading