-
Notifications
You must be signed in to change notification settings - Fork 89
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
feat(qttypes): Do more wrappers around QVariant #136
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only made a partial review so far.
In general, i don't think it is worth it to try to exhaustively wrap every call and expose the full API.
The philosophy behind this whole project is to come up with a idiomatic API to use QML from Rust without having to write C++ or learn Qt. And to do so in a most efficient way.
Yes, we also want to keep close to the Qt API and i understand this is a conflicting goal, but in doubt, better have idiomatic rust API than blindly imitate Qt.
For example, all these conversion might not be worth it when there is the QMetaType trait doing them. And also the From and Into trait.
Wouldn't it be better if all the QVariant::toSomething() methods return Option in rust, by checking for canConvert() first? Would be much more Rusty way, at the cost of deviating from Qt origins.
Yes, i agree, it should
QVariant(const Something &val) constructors actually take values by references, they never really consume original value. Wouldn't it make more sense to add & to all those impl From for QVariant i.e. impl From<&Something> for QVariant?
It all depends if the function can sonsume (take ownership) of the memory or not. If not, then it might indeed be better to take value by reference.
Comments with links are missing from implicitly generated basic traits by cpp! macro. No idea what to do.
I think that's fine. These links wouldn't really help anyone IMHO.
Do we really want all those TODOs stay there probably forever?
No.
} | ||
|
||
// TODO: for this to work, we need QMetaType be available in qttypes package. | ||
// TODO: It would create a dependency cycle at this point. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we don't need to expose this function in this crate.
The function in the QMetaType trait should be enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
}) | ||
} | ||
|
||
// TODO?: [static] template <typename Types> QVariant QVariant::fromStdVariant(const std::variant<Types...> &value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, we don't have to replicate the whole API, only the API that make sense. This clearly make no sense in rust.
Same for the function that operates on the QMetaType which are better done by the QMetaType trait.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed.
/// Wrapper around [`swap(QVariant &other)`][method] method. | ||
/// | ||
/// [method]: https://doc.qt.io/qt-5/qvariant.html#swap | ||
pub fn swap(&mut self, other: &mut QVariant) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function also doesn't make sense in rust. One would use the std::mem::swap function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, kinda thought about std::mem::swap as well. It was so weird to work with reference to a pointer passed through FFI, that I even wrote a test to make sure everything matched up.
/// | ||
/// [`to_qbytearray()`]: Self::to_qbytearray() | ||
/// [implicit sharing]: https://doc.qt.io/qt-5/implicit-sharing.html | ||
pub fn to_bytes(&self) -> Vec<u8> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a particular use case for this function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Idk, just a shortcut, a convenience. Kinda making it more Rust-oriented and usable for non-Qt code.
I wanted to name it to_vec
, but it would cause all sorts of conflicts with toList() -> QVariantList
.
/// Wrapper around [`toDouble(bool *ok)`][method] method. | ||
/// | ||
/// [method]: https://doc.qt.io/qt-5/qvariant.html#toDouble | ||
pub fn to_double(&self) -> Option<f64> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess i'd call this to_f64
instread
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then, it would apply to all the others in family as well
Overall, a big thanks for such a fast review. I'm going to call it a day for now, re-think about it overnight, and come up with (suggested) changes tomorrow. Side note: As much as I'd like to conduct it in Gerrit-style, GitHub doesn't allow viewing inter-patchset diff, so I'll be adding new commits, but manually squash before final merge. |
I've finally had a true weekend, phew. So didn't have a chance to code, but gave it a few thoughts. I totally agree that in order to make it the Rust way, all those to_something() methods should return an optional value. In fact, they can be implemented as TryInto trait with unit Once thing that still bothers me, is that
which means, for those methods, it might be reasonable to check either both or only Will try my best to push updates ASAP. |
Wanted to implement pub fn to_hash_map(&self) -> HashMap<String, QVariant> {
// QVariant::toHash() is guaranteed to be cheap, fast and constant time O(1).
// If `self` is not a QHash, an empty one will be returned.
let size = cpp!(unsafe [self as "const QVariant*"] -> i32 as "int" {
return self->toHash().size();
});
let mut map = HashMap::with_capacity(size as usize);
let m = &mut map; // Rust's HashMap is opaque for C++
cpp!(unsafe [self as "const QVariant*", m as "void*"] {
using namespace std;
QVariantHash hash = self->toHash();
QHashIterator<QString, QVariant> i(hash);
while (i.hasNext()) {
i.next();
QString key = i.key();
QVariant value = i.value();
rust!(QVariant_toHash_insert [
m: &mut HashMap<String, QVariant> as "void*",
key: &QString as "QString&",
value: &QVariant as "QVariant&"
] {
m.insert(key.clone().into(), value.clone());
});
}
});
map
} |
Wrapped converters to and constructors from all available types. The rest of them are marked as TODO comments for future implementers. Methods and constructors, which I'm not sure about whether they are needed to be exposed in Rust at all, are marked with `TODO?:` prefix. Constructors are reordered according to the official Qt documentation for ease of code navigation. Return types are shortened to `Self`. Fixed some misleading links to upstream docs in From<{i,u}{32,64}> impls. Some wrappers are impossible to write at this point without introducing QMetaType and its Type enum in this crate or its dependencies, as currently it would create a circular deps between qmetaobject and qttypes crates. Such hypothetical implementations are left commented out for future ideas.
For now, it was only a rebase. Not worth looking into. |
Wrapped converters to and constructors from all available types. The rest
of them are marked as TODO comments for future implementers.
Methods and constructors, which I'm not sure about whether they are needed
to be exposed in Rust at all, are marked with
TODO?:
prefix.Constructors are reordered according to the official Qt documentation
for ease of code navigation. Return types are shortened to
Self
.Fixed some misleading links to upstream docs in From<{i,u}{32,64}> impls.
Some wrappers are impossible to write at this point without introducing
QMetaType and its Type enum in this crate or its dependencies, as currently
it would create a circular deps between qmetaobject and qttypes crates.
Such hypothetical implementations are left commented out for future ideas.
I acknowledge that this is some controversial big boy commit droppin' here like a bomb. I ask to carefully review it, and discuss key design concerns outlined below.
QVariant::toSomething()
methods returnOption<Something>
in rust, by checking forcanConvert()
first? Would be much more Rusty way, at the cost of deviating from Qt origins.QVariant(const Something &val)
constructors actually take values by references, they never really consume original value. Wouldn't it make more sense to add&
to all thoseimpl From<Something> for QVariant
i.e.impl From<&Something> for QVariant
?cpp!
macro. No idea what to do.