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-lib: use unique ptr types and don't wrap #166

Merged
merged 3 commits into from
Aug 2, 2022
Merged
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
51 changes: 40 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,48 @@ For more complex examples navigate to the [examples folder](./examples) where th
Below is an example of Rust code that exposes a QObject with two properties and four invokable methods to Qt.

```rust
use serde::{Deserialize, Serialize};

// Represent the Data struct below with serde friendly types, so we can (de)serialize it
#[derive(Deserialize, Serialize)]
pub struct DataSerde {
Be-ing marked this conversation as resolved.
Show resolved Hide resolved
number: i32,
string: String,
}

impl From<Data> for DataSerde {
fn from(value: Data) -> DataSerde {
DataSerde {
number: value.number,
string: value.string.to_string(),
}
}
}

#[cxx_qt::bridge]
mod my_object {
use serde::{Deserialize, Serialize};
use super::DataSerde;

const DEFAULT_STR: &str = r#"{"number": 1, "string": "Hello World!"}"#;

#[derive(Deserialize, Serialize)]
pub struct Data {
number: i32,
string: String,
pub number: i32,
pub string: UniquePtr<QString>,
}

impl Default for Data {
fn default() -> Self {
serde_json::from_str(DEFAULT_STR).unwrap()
let data_serde: DataSerde = serde_json::from_str(DEFAULT_STR).unwrap();
data_serde.into()
}
}

impl From<DataSerde> for Data {
fn from(value: DataSerde) -> Data {
Data {
number: value.number,
string: QString::from_str(&value.string),
}
}
}

Expand All @@ -63,21 +90,23 @@ mod my_object {

#[invokable]
pub fn reset(&self, cpp: &mut CppObj) {
let data: Data = serde_json::from_str(DEFAULT_STR).unwrap();
cpp.grab_values_from_data(data);
let data: DataSerde = serde_json::from_str(DEFAULT_STR).unwrap();
cpp.grab_values_from_data(data.into());
}

#[invokable]
pub fn serialize(&self, cpp: &mut CppObj) -> String {
pub fn serialize(&self, cpp: &mut CppObj) -> UniquePtr<QString> {
let data = Data::from(cpp);
serde_json::to_string(&data).unwrap()
let data_serde = DataSerde::from(data);
let data_string = serde_json::to_string(&data_serde).unwrap();
QString::from_str(&data_string)
}

#[invokable]
pub fn grab_values(&self, cpp: &mut CppObj) {
let string = r#"{"number": 2, "string": "Goodbye!"}"#;
let data: Data = serde_json::from_str(string).unwrap();
cpp.grab_values_from_data(data);
let data: DataSerde = serde_json::from_str(string).unwrap();
cpp.grab_values_from_data(data.into());
}
}
}
Expand Down
8 changes: 3 additions & 5 deletions book/src/concepts/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,15 @@ Note that when they are used as a parameter type in invokables they should be pa

### Custom Opaque Types

Custom opaque types wrap a unique pointer to the C++ type, they are used in the same way as custom trivial types but CXX-Qt automatically writes wrappers to convert to and from a C++ unique pointer of the type to a Rust wrapper of the type.
Custom [opaque types](https://en.wikipedia.org/wiki/Opaque_data_type) represent an opaque C++ type, they can be used for properties, parameters or return types in invokables, and parameters in signals. However when the object is being passed from Rust to C++ (eg properties, return types, or signals) they must be `UniquePtr<T>`, and for other use cases they must be by reference (either `&T` or `Pin<&mut T>`).

On the rust side they appear as the cxx_qt_lib helper type.

Note that when they are used as a parameter type in invokables they should be passed as a reference, eg `color: &QColor`, and when they are a property or return type they should be a value, eg `QColor`. Also for strings `&str` should be used when passed as a reference and `String` when passed as a value.
Note that when they are used as a parameter type in invokables they should be passed as a reference, eg `color: &QColor`, and when they are a property, return type, or signal they should be a `UniquePtr`, eg `UniquePtr<QColor>`.

| Rust Type | C++ Type |
|-----------|----------|
| cxx_qt_lib::QColor | QColor |
| cxx_qt_lib::QDateTime | QDateTime |
| String or &str | QString |
| cxx_qt_lib::QString | QString |
| cxx_qt_lib::QUrl | QUrl |
| cxx_qt_lib::QVariant | QVariant |

Expand Down
2 changes: 1 addition & 1 deletion book/src/getting-started/2-our-first-cxx-qt-module.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Note that the data types we use here are normal Rust data types.
CXX-Qt will automatically convert these types to their C++/Qt equivalent.
In our case that means:
- `number: i32` -> `int number`
- `string: String` -> `QString string`\
- `string: UniquePtr<QString>` -> `QString string`\
For more details on the available types, see the [Qt types page](../concepts/types.md).

You might have also noticed the `#[derive(Default)]` here.
Expand Down
54 changes: 40 additions & 14 deletions cxx-qt-gen/src/extract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,16 @@ pub(crate) enum QtTypes {
QRectF,
QSize,
QSizeF,
String,
Str,
QString,
QTime,
QUrl,
QVariant,
U8,
U16,
U32,
UniquePtr {
inner: Box<QtTypes>,
},
Unknown,
}

Expand All @@ -84,11 +86,7 @@ impl QtTypes {
pub(crate) fn is_opaque(&self) -> bool {
match self {
Self::CppObj { .. } => true,
Self::QColor => true,
Self::QDateTime => true,
Self::String | Self::Str => true,
Self::QUrl => true,
Self::QVariant => true,
Self::UniquePtr { .. } => true,
_others => false,
}
}
Expand Down Expand Up @@ -252,8 +250,7 @@ fn extract_qt_type(
"QRectF" => Ok(QtTypes::QRectF),
"QSize" => Ok(QtTypes::QSize),
"QSizeF" => Ok(QtTypes::QSizeF),
"str" => Ok(QtTypes::Str),
"String" => Ok(QtTypes::String),
"QString" => Ok(QtTypes::QString),
"QTime" => Ok(QtTypes::QTime),
"QUrl" => Ok(QtTypes::QUrl),
"QVariant" => Ok(QtTypes::QVariant),
Expand Down Expand Up @@ -314,6 +311,16 @@ fn extract_qt_type(
.to_case(Case::Pascal)
),
})
// This is a UniquePtr<T> field
} else if idents.len() > 1 && idents.first().unwrap().to_string().as_str() == "UniquePtr" {
Ok(QtTypes::UniquePtr {
inner: Box::new(extract_qt_type(
&idents[1..],
original_ty,
cpp_namespace_prefix,
qt_ident,
)?),
})
// This is an unknown type that did not start with crate and has multiple parts
} else {
// We can assume that idents has an entry at index zero, because it is not empty
Expand All @@ -323,6 +330,20 @@ fn extract_qt_type(

/// Converts a given path to a vector of idents
fn path_to_idents(path: &syn::Path) -> Result<Vec<Ident>, ExtractTypeIdentError> {
// We do support UniquePtr<T> for now
if let Some(segment) = path.segments.first() {
if segment.ident == "UniquePtr" {
if let PathArguments::AngleBracketed(angled) = &segment.arguments {
if let Some(GenericArgument::Type(Type::Path(type_path))) = angled.args.first() {
return path_to_idents(&type_path.path).map(|mut idents| {
idents.insert(0, quote::format_ident!("UniquePtr"));
idents
});
}
}
}
}

path.segments
.iter()
.map(|segment| {
Expand All @@ -331,10 +352,10 @@ fn path_to_idents(path: &syn::Path) -> Result<Vec<Ident>, ExtractTypeIdentError>
// eg we do not support AngleBracketed - the <'a, T> in std::slice::iter<'a, T>
// eg we do not support Parenthesized - the (A, B) -> C in Fn(A, B) -> C
if segment.arguments == PathArguments::None {
Ok(segment.ident.to_owned())
} else {
Err(ExtractTypeIdentError::InvalidArguments(segment.span()))
return Ok(segment.ident.to_owned());
}

Err(ExtractTypeIdentError::InvalidArguments(segment.span()))
})
.collect::<Result<Vec<Ident>, ExtractTypeIdentError>>()
}
Expand Down Expand Up @@ -1485,8 +1506,9 @@ mod tests {
let prop_second = &qobject.properties[1];
assert_eq!(prop_second.ident.cpp_ident.to_string(), "opaque");
assert_eq!(prop_second.ident.rust_ident.to_string(), "opaque");
assert_eq!(prop_second.type_ident.idents.len(), 1);
assert_eq!(prop_second.type_ident.idents[0].to_string(), "QColor");
assert_eq!(prop_second.type_ident.idents.len(), 2);
assert_eq!(prop_second.type_ident.idents[0].to_string(), "UniquePtr");
assert_eq!(prop_second.type_ident.idents[1].to_string(), "QColor");
assert!(!prop_second.type_ident.is_ref);

assert!(prop_second.getter.is_some());
Expand Down Expand Up @@ -1583,6 +1605,10 @@ mod tests {
assert_eq!(qobject.signals[1].parameters[1].ident.to_string(), "second");
assert_eq!(
qobject.signals[1].parameters[1].type_ident.idents[0].to_string(),
"UniquePtr"
);
assert_eq!(
qobject.signals[1].parameters[1].type_ident.idents[1].to_string(),
"QVariant"
);
assert_eq!(qobject.signals[1].parameters[2].ident.to_string(), "third");
Expand Down
17 changes: 13 additions & 4 deletions cxx-qt-gen/src/gen_cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,11 @@ impl CppType for QtTypes {
Self::QRectF => vec!["#include <QtCore/QRectF>".to_owned()],
Self::QSize => vec!["#include <QtCore/QSize>".to_owned()],
Self::QSizeF => vec!["#include <QtCore/QSizeF>".to_owned()],
Self::String | Self::Str => vec!["#include <QtCore/QString>".to_owned()],
Self::QString => vec!["#include <QtCore/QString>".to_owned()],
Self::QTime => vec!["#include <QtCore/QTime>".to_owned()],
Self::QUrl => vec!["#include <QtCore/QUrl>".to_owned()],
Self::QVariant => vec!["#include <QtCore/QVariant>".to_owned()],
Self::UniquePtr { inner } => inner.include_paths(),
_others => vec![],
}
}
Expand All @@ -117,11 +118,12 @@ impl CppType for QtTypes {
Self::QRectF => true,
Self::QSize => true,
Self::QSizeF => true,
Self::Str | Self::String => true,
Self::QString => true,
Self::QTime => true,
Self::QUrl => true,
Self::QVariant => true,
Self::U8 | Self::U16 | Self::U32 => false,
Self::UniquePtr { .. } => true,
_other => unreachable!(),
}
}
Expand Down Expand Up @@ -166,11 +168,12 @@ impl CppType for QtTypes {
Self::QRectF => true,
Self::QSize => true,
Self::QSizeF => true,
Self::Str | Self::String => true,
Self::QString => true,
Self::QTime => true,
Self::QUrl => true,
Self::QVariant => true,
Self::U8 | Self::U16 | Self::U32 => false,
Self::UniquePtr { .. } => true,
_other => unreachable!(),
}
}
Expand Down Expand Up @@ -206,13 +209,19 @@ impl CppType for QtTypes {
Self::QRectF => "QRectF",
Self::QSize => "QSize",
Self::QSizeF => "QSizeF",
Self::Str | Self::String => "QString",
Self::QString => "QString",
Self::QTime => "QTime",
Self::QUrl => "QUrl",
Self::QVariant => "QVariant",
Self::U8 => "quint8",
Self::U16 => "quint16",
Self::U32 => "quint32",
// TODO: for now always automatically convert UniquePtr<T> to T
// in C++, later this will require a macro attribute to do this
// eg for properties, invokable returns, signals
//
// But this may be changed once the generation pattern matching has been removed
Self::UniquePtr { inner } => inner.type_ident(),
_other => unreachable!(),
}
}
Expand Down
Loading