Skip to content

Commit

Permalink
cxx-qt-lib: define a QSet<T> with QSetElement trait
Browse files Browse the repository at this point in the history
Also define all primitive types and cxx_qt_lib types as T.

Related to #347
  • Loading branch information
ahayzen-kdab committed Nov 16, 2022
1 parent 173f936 commit 21ebcfe
Show file tree
Hide file tree
Showing 29 changed files with 1,186 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,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>`

## [0.4.0](https://github.com/KDAB/cxx-qt/compare/v0.3.0...v0.4.0) - 2022-10-28

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
62 changes: 62 additions & 0 deletions crates/cxx-qt-lib-headers/include/qset.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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"

template<typename T>
struct rust::IsRelocatable<QSet<T>> : std::true_type
{
};

// Note on the Rust side we rename the methods to "clear" or "len" etc
// so we need to put each set in it's own namespace otherwise the generated
// CXX code collides.
#define CXX_QT_QSET_METHODS(typeName, name) \
using QSet_##name = QSet<typeName>; \
\
namespace rust { \
namespace cxxqtlib1 { \
namespace qset_##name \
{ \
void qset_clear_##name(QSet_##name& s) noexcept; \
QSet_##name qset_clone_##name(const QSet_##name& s) noexcept; \
bool qset_contains_##name(const QSet_##name& s, \
const typeName& value) noexcept; \
QSet_##name qset_default_##name() noexcept; \
void qset_drop_##name(QSet_##name& s) noexcept; \
const typeName& qset_get_unchecked_##name(const QSet_##name& s, \
::std::size_t pos) noexcept; \
void qset_insert_##name(QSet_##name& s, const typeName& value) noexcept; \
std::size_t qset_len_##name(const QSet_##name& s) noexcept; \
bool qset_remove_##name(QSet_##name& s, const typeName& value) noexcept; \
} \
} \
}

CXX_QT_QSET_METHODS(bool, bool);
CXX_QT_QSET_METHODS(float, f32);
CXX_QT_QSET_METHODS(double, f64);
CXX_QT_QSET_METHODS(::qint8, i8);
CXX_QT_QSET_METHODS(::qint16, i16);
CXX_QT_QSET_METHODS(::qint32, i32);
CXX_QT_QSET_METHODS(::QDate, QDate);
CXX_QT_QSET_METHODS(::QDateTime, QDateTime);
CXX_QT_QSET_METHODS(::QString, QString);
CXX_QT_QSET_METHODS(::QTime, QTime);
CXX_QT_QSET_METHODS(::QUrl, QUrl);
CXX_QT_QSET_METHODS(::quint8, u8);
CXX_QT_QSET_METHODS(::quint16, u16);
CXX_QT_QSET_METHODS(::quint32, u32);
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
39 changes: 35 additions & 4 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,11 +58,28 @@ fn main() {
}

let mut builder = cxx_build::bridges(
bridges
rust_bridges
.iter()
.map(|bridge| format!("src/types/{}.rs", bridge)),
);
for bridge in bridges {

let cpp_bridges = [
"qcolor",
"qdate",
"qdatetime",
"qpoint",
"qpointf",
"qrect",
"qrectf",
"qset",
"qsize",
"qsizef",
"qstring",
"qtime",
"qurl",
"qvariant",
];
for bridge in cpp_bridges {
builder.file(format!("src/types/{}.cpp", bridge));
println!("cargo:rerun-if-changed=src/types/{}.cpp", bridge);
}
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
97 changes: 97 additions & 0 deletions crates/cxx-qt-lib/src/types/qset.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// 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
#include "cxx-qt-lib/qset.h"

#include "assertion_utils.h"

#define CXX_QT_QSET_ASSERTS(typeName, name) \
assert_alignment_and_size( \
QSet_##name, alignof(std::size_t), sizeof(std::size_t)); \
\
static_assert(!std::is_trivially_copy_assignable<QSet_##name>::value); \
static_assert(!std::is_trivially_copy_constructible<QSet_##name>::value); \
static_assert(!std::is_trivially_destructible<QSet_##name>::value); \
\
static_assert(QTypeInfo<QSet_##name>::isRelocatable); \
\
static_assert(std::is_copy_assignable<typeName>::value); \
static_assert(std::is_copy_constructible<typeName>::value);

#define CXX_QT_QSET_METHODS_IMPL(typeName, name) \
CXX_QT_QSET_ASSERTS(typeName, name); \
\
namespace rust { \
namespace cxxqtlib1 { \
namespace qset_##name \
{ \
void qset_clear_##name(QSet_##name& s) noexcept \
{ \
s.clear(); \
} \
\
QSet_##name qset_clone_##name(const QSet_##name& s) noexcept \
{ \
return QSet(s); \
} \
\
bool qset_contains_##name(const QSet_##name& s, \
const typeName& value) noexcept \
{ \
return s.contains(value); \
} \
\
QSet_##name qset_default_##name() noexcept \
{ \
return QSet_##name(); \
} \
\
void qset_drop_##name(QSet_##name& s) noexcept \
{ \
s.~QSet_##name(); \
} \
\
const typeName& qset_get_unchecked_##name(const QSet_##name& 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; \
} \
\
void qset_insert_##name(QSet_##name& s, const typeName& value) noexcept \
{ \
s.insert(value); \
} \
\
::std::size_t qset_len_##name(const QSet_##name& s) noexcept \
{ \
return static_cast<::std::size_t>(s.size()); \
} \
\
bool qset_remove_##name(QSet_##name& s, const typeName& value) noexcept \
{ \
return s.remove(value); \
} \
} \
} \
}

CXX_QT_QSET_METHODS_IMPL(bool, bool);
CXX_QT_QSET_METHODS_IMPL(float, f32);
CXX_QT_QSET_METHODS_IMPL(double, f64);
CXX_QT_QSET_METHODS_IMPL(::qint8, i8);
CXX_QT_QSET_METHODS_IMPL(::qint16, i16);
CXX_QT_QSET_METHODS_IMPL(::qint32, i32);
CXX_QT_QSET_METHODS_IMPL(::QDate, QDate);
CXX_QT_QSET_METHODS_IMPL(::QDateTime, QDateTime);
CXX_QT_QSET_METHODS_IMPL(::QString, QString);
CXX_QT_QSET_METHODS_IMPL(::QTime, QTime);
CXX_QT_QSET_METHODS_IMPL(::QUrl, QUrl);
CXX_QT_QSET_METHODS_IMPL(::quint8, u8);
CXX_QT_QSET_METHODS_IMPL(::quint16, u16);
CXX_QT_QSET_METHODS_IMPL(::quint32, u32);
110 changes: 110 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,110 @@
#!/usr/bin/env bash

# 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>;
}
#[namespace = "rust::cxxqtlib1::qset_$1"]
unsafe extern "C++" {
#[rust_name = "clear"]
fn qset_clear_$1(_: &mut QSet_$1);
#[rust_name = "clone"]
fn qset_clone_$1(_: &QSet_$1) -> QSet_$1;
#[rust_name = "contains"]
fn qset_contains_$1(_: &QSet_$1, _: &$1) -> bool;
#[rust_name = "default"]
fn qset_default_$1() -> QSet_$1;
#[rust_name = "drop"]
fn qset_drop_$1(_: &mut QSet_$1);
#[rust_name = "get_unchecked"]
#[allow(clippy::needless_lifetimes)]
unsafe fn qset_get_unchecked_$1<'a>(set: &'a QSet_$1, pos: usize) -> &'a $1;
#[rust_name = "insert"]
fn qset_insert_$1(_: &mut QSet_$1, _: &$1);
#[rust_name = "len"]
fn qset_len_$1(_: &QSet_$1) -> usize;
#[rust_name = "remove"]
fn qset_remove_$1(_: &mut QSet_$1, _: &$1) -> bool;
}
}
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>;
}
#[namespace = "rust::cxxqtlib1::qset_$1"]
unsafe extern "C++" {
#[rust_name = "clear"]
fn qset_clear_$1(_: &mut QSet_$1);
#[rust_name = "clone"]
fn qset_clone_$1(_: &QSet_$1) -> QSet_$1;
#[rust_name = "contains"]
fn qset_contains_$1(_: &QSet_$1, _: &$1) -> bool;
#[rust_name = "default"]
fn qset_default_$1() -> QSet_$1;
#[rust_name = "drop"]
fn qset_drop_$1(_: &mut QSet_$1);
#[rust_name = "get_unchecked"]
unsafe fn qset_get_unchecked_$1(set: &QSet_$1, pos: usize) -> &$1;
#[rust_name = "insert"]
fn qset_insert_$1(_: &mut QSet_$1, _: &$1);
#[rust_name = "len"]
fn qset_len_$1(_: &QSet_$1) -> usize;
#[rust_name = "remove"]
fn qset_remove_$1(_: &mut QSet_$1, _: &$1) -> bool;
}
}
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

0 comments on commit 21ebcfe

Please sign in to comment.