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: build a QSet<T> #350

Merged
merged 4 commits into from
Dec 1, 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Multiple QObjects can be defined in one bridge
- Support for container types: `QSet<T>`

### Fixed

Expand Down
13 changes: 13 additions & 0 deletions book/src/concepts/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ The `cxx-qt-lib` crate provides CXX bindings for common Qt types.

Use the [`cxx-qt-lib` Docs](https://docs.rs/cxx-qt-lib/latest/cxx_qt_lib/) to explore the available types.

### Container Types

The `cxx-qt-lib` crate has containers types, such as `QSet<T>`.

To use these define a templated type in the CXX bridge, but note that the type
name must be `QSet_T` as this needs to match the name in C++ code.

So for `QSet<i32>` the type name should be `QSet_i32`.

```rust,ignore
{{#include ../../../tests/qt_types_standalone/rust/src/qset.rs:book_qset}}
```

## Defining a Custom Type

Any types that are valid CXX types should be usable with CXX-Qt as well.
Expand Down
95 changes: 95 additions & 0 deletions crates/cxx-qt-lib-headers/include/qset.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// clang-format off
// SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
// clang-format on
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
//
// SPDX-License-Identifier: MIT OR Apache-2.0
#pragma once

#include <QtCore/QSet>

#include <QtCore/QDate>
#include <QtCore/QDateTime>
#include <QtCore/QString>
#include <QtCore/QTime>
#include <QtCore/QUrl>

#include "rust/cxx.h"

// This has static asserts in the cpp file to ensure this is valid.
template<typename T>
struct rust::IsRelocatable<QSet<T>> : std::true_type
LeonMatthesKDAB marked this conversation as resolved.
Show resolved Hide resolved
{
};

namespace rust {
namespace cxxqtlib1 {

template<typename T>
QSet<T>
qset_clone(const QSet<T>& s) noexcept
{
return QSet(s);
}

template<typename T>
QSet<T>
qset_default() noexcept
LeonMatthesKDAB marked this conversation as resolved.
Show resolved Hide resolved
{
return QSet<T>();
}

template<typename T>
void
qset_drop(QSet<T>& s) noexcept
{
s.~QSet<T>();
}

template<typename T>
const T&
qset_get_unchecked(const QSet<T>& s, ::std::size_t pos) noexcept
{
Q_ASSERT(pos < static_cast<::std::size_t>(s.size()));
auto it = s.cbegin();
std::advance(it, pos);
return *it;
}

template<typename T>
void
qset_insert(QSet<T>& s, const T& value) noexcept
{
s.insert(value);
}

template<typename T>
::std::size_t
qset_len(const QSet<T>& s) noexcept
{
// In Qt 5 the type was int now it is qsizetype, so we need to ensure the type
// is the same for CXX
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
return s.size();
#else
return static_cast<::std ::size_t>(s.size());
#endif
}

}
}

using QSet_bool = QSet<bool>;
using QSet_f32 = QSet<float>;
using QSet_f64 = QSet<double>;
using QSet_i8 = QSet<::qint8>;
using QSet_i16 = QSet<::qint16>;
using QSet_i32 = QSet<::qint32>;
using QSet_QDate = QSet<::QDate>;
using QSet_QDateTime = QSet<::QDateTime>;
using QSet_QString = QSet<::QString>;
using QSet_QTime = QSet<::QTime>;
using QSet_QUrl = QSet<::QUrl>;
using QSet_u8 = QSet<::quint8>;
using QSet_u16 = QSet<::quint16>;
using QSet_u32 = QSet<::quint32>;
3 changes: 2 additions & 1 deletion crates/cxx-qt-lib-headers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

use std::{fs::File, io::Write, path::Path};

static HEADERS: [(&str, &str); 15] = [
static HEADERS: [(&str, &str); 16] = [
(include_str!("../include/convert.h"), "convert.h"),
(include_str!("../include/cxxqt_thread.h"), "cxxqt_thread.h"),
(include_str!("../include/qcolor.h"), "qcolor.h"),
Expand All @@ -20,6 +20,7 @@ static HEADERS: [(&str, &str); 15] = [
(include_str!("../include/qpointf.h"), "qpointf.h"),
(include_str!("../include/qrect.h"), "qrect.h"),
(include_str!("../include/qrectf.h"), "qrectf.h"),
(include_str!("../include/qset.h"), "qset.h"),
(include_str!("../include/qsize.h"), "qsize.h"),
(include_str!("../include/qsizef.h"), "qsizef.h"),
(include_str!("../include/qstring.h"), "qstring.h"),
Expand Down
43 changes: 37 additions & 6 deletions crates/cxx-qt-lib/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,36 @@ fn main() {
qtbuild.version().major
);

let bridges = [
let rust_bridges = [
"qcolor",
"qdate",
"qdatetime",
"qpoint",
"qpointf",
"qrect",
"qrectf",
"qset/qset_bool",
"qset/qset_f32",
"qset/qset_f64",
"qset/qset_i8",
"qset/qset_i16",
"qset/qset_i32",
"qset/qset_qdate",
"qset/qset_qdatetime",
"qset/qset_qstring",
"qset/qset_qtime",
"qset/qset_qurl",
"qset/qset_u8",
"qset/qset_u16",
"qset/qset_u32",
"qsize",
"qsizef",
"qstring",
"qtime",
"qurl",
"qvariant",
];
for bridge in bridges {
for bridge in rust_bridges {
println!("cargo:rerun-if-changed=src/types/{}.rs", bridge);
}

Expand All @@ -44,13 +58,30 @@ fn main() {
}

let mut builder = cxx_build::bridges(
bridges
rust_bridges
.iter()
.map(|bridge| format!("src/types/{}.rs", bridge)),
);
for bridge in bridges {
builder.file(format!("src/types/{}.cpp", bridge));
println!("cargo:rerun-if-changed=src/types/{}.cpp", bridge);

let cpp_files = [
"qcolor",
"qdate",
"qdatetime",
"qpoint",
"qpointf",
"qrect",
"qrectf",
"qset/qset",
"qsize",
"qsizef",
"qstring",
"qtime",
"qurl",
"qvariant",
];
for cpp_file in cpp_files {
builder.file(format!("src/types/{}.cpp", cpp_file));
println!("cargo:rerun-if-changed=src/types/{}.cpp", cpp_file);
}
builder.file("src/qt_types.cpp");
println!("cargo:rerun-if-changed=src/qt_types.cpp");
Expand Down
3 changes: 3 additions & 0 deletions crates/cxx-qt-lib/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ pub use qrect::QRect;
mod qrectf;
pub use qrectf::QRectF;

mod qset;
pub use qset::{QSet, QSetElement};

mod qsize;
pub use qsize::QSize;

Expand Down
164 changes: 164 additions & 0 deletions crates/cxx-qt-lib/src/types/qset/generate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
#!/usr/bin/env bash
ahayzen-kdab marked this conversation as resolved.
Show resolved Hide resolved

# SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
# SPDX-FileContributor: Andrew Hayzen <[email protected]>
#
# SPDX-License-Identifier: MIT OR Apache-2.0

set -e

SCRIPT=$(realpath "$0")
SCRIPTPATH=$(dirname "$SCRIPT")

function generate_bridge_primitive() {
tee "$SCRIPTPATH/qset_$1.rs" <<EOF
// SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
//
// SPDX-License-Identifier: MIT OR Apache-2.0

#[cxx::bridge]
pub mod ffi {
unsafe extern "C++" {
include!("cxx-qt-lib/qset.h");
type QSet_$1 = crate::QSet<$1>;
}

unsafe extern "C++" {
LeonMatthesKDAB marked this conversation as resolved.
Show resolved Hide resolved
#[rust_name = "cxx_clear"]
fn clear(self: &mut QSet_$1);
#[rust_name = "cxx_contains"]
fn contains(self: &QSet_$1, _: &$1) -> bool;
#[rust_name = "cxx_remove"]
fn remove(self: &mut QSet_$1, _: &$1) -> bool;
}

#[namespace = "rust::cxxqtlib1"]
unsafe extern "C++" {
#[rust_name = "clone_$1"]
fn qset_clone(_: &QSet_$1) -> QSet_$1;
#[rust_name = "default_$1"]
fn qset_default() -> QSet_$1;
#[rust_name = "drop_$1"]
fn qset_drop(_: &mut QSet_$1);
#[rust_name = "get_unchecked_$1"]
#[allow(clippy::needless_lifetimes)]
unsafe fn qset_get_unchecked<'a>(set: &'a QSet_$1, pos: usize) -> &'a $1;
#[rust_name = "insert_$1"]
fn qset_insert(_: &mut QSet_$1, _: &$1);
#[rust_name = "len_$1"]
fn qset_len(_: &QSet_$1) -> usize;
}
}

pub(crate) fn clone(s: &ffi::QSet_$1) -> ffi::QSet_$1 {
ffi::clone_$1(s)
}

pub(crate) fn default() -> ffi::QSet_$1 {
ffi::default_$1()
}

pub(crate) fn drop(s: &mut ffi::QSet_$1) {
ffi::drop_$1(s);
}

pub(crate) unsafe fn get_unchecked(s: &ffi::QSet_$1, pos: usize) -> &$1 {
ffi::get_unchecked_$1(s, pos)
}

pub(crate) fn insert(s: &mut ffi::QSet_$1, value: &$1) {
ffi::insert_$1(s, value);
}

pub(crate) fn len(s: &ffi::QSet_$1) -> usize {
ffi::len_$1(s)
}
EOF
rustfmt "$SCRIPTPATH/qset_$1.rs"
}

function generate_bridge_qt() {
tee "$SCRIPTPATH/qset_$2.rs" <<EOF
// SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
//
// SPDX-License-Identifier: MIT OR Apache-2.0

#[cxx::bridge]
pub mod ffi {
unsafe extern "C++" {
include!("cxx-qt-lib/$2.h");
type $1 = crate::$1;

include!("cxx-qt-lib/qset.h");
type QSet_$1 = crate::QSet<$1>;
}

unsafe extern "C++" {
#[rust_name = "cxx_clear"]
fn clear(self: &mut QSet_$1);
#[rust_name = "cxx_contains"]
fn contains(self: &QSet_$1, _: &$1) -> bool;
#[rust_name = "cxx_remove"]
fn remove(self: &mut QSet_$1, _: &$1) -> bool;
}

#[namespace = "rust::cxxqtlib1"]
unsafe extern "C++" {
#[rust_name = "clone_$1"]
fn qset_clone(_: &QSet_$1) -> QSet_$1;
#[rust_name = "default_$1"]
fn qset_default() -> QSet_$1;
#[rust_name = "drop_$1"]
fn qset_drop(_: &mut QSet_$1);
#[rust_name = "get_unchecked_$1"]
unsafe fn qset_get_unchecked(set: &QSet_$1, pos: usize) -> &$1;
#[rust_name = "insert_$1"]
fn qset_insert(_: &mut QSet_$1, _: &$1);
#[rust_name = "len_$1"]
fn qset_len(_: &QSet_$1) -> usize;
}
}

pub(crate) fn clone(s: &ffi::QSet_$1) -> ffi::QSet_$1 {
ffi::clone_$1(s)
}

pub(crate) fn default() -> ffi::QSet_$1 {
ffi::default_$1()
}

pub(crate) fn drop(s: &mut ffi::QSet_$1) {
ffi::drop_$1(s);
}

pub(crate) unsafe fn get_unchecked(s: &ffi::QSet_$1, pos: usize) -> &ffi::$1 {
ffi::get_unchecked_$1(s, pos)
}

pub(crate) fn insert(s: &mut ffi::QSet_$1, value: &ffi::$1) {
ffi::insert_$1(s, value);
}

pub(crate) fn len(s: &ffi::QSet_$1) -> usize {
ffi::len_$1(s)
}
EOF
rustfmt "$SCRIPTPATH/qset_$2.rs"
}

generate_bridge_primitive "bool"
generate_bridge_primitive "f32"
generate_bridge_primitive "f64"
generate_bridge_primitive "i8"
generate_bridge_primitive "i16"
generate_bridge_primitive "i32"
generate_bridge_qt "QDate" "qdate"
generate_bridge_qt "QDateTime" "qdatetime"
generate_bridge_qt "QString" "qstring"
generate_bridge_qt "QTime" "qtime"
generate_bridge_qt "QUrl" "qurl"
generate_bridge_primitive "u8"
generate_bridge_primitive "u16"
generate_bridge_primitive "u32"
Loading