[Development] Converting types in Qt
Jędrzej Nowacki
jedrzej.nowacki at digia.com
Tue Jul 15 08:55:29 CEST 2014
Hi,
I would like to discuss type conversions in Qt. As you may know, Qt has
the ability to convert a known type to another known type. This works for
trivial cases like, for example, "int" to "long", but also for more complex
ones like "QByteArray" to "QString" or "CustomType" to "OtherCustomType". Type
conversion in Qt happens mostly without user interaction, for example in
QObject introspection or in QML, but it also can be used through public API:
- QVariant::convert -> converts wrapped value to target type
- QVariant::canConvert -> fast check if it may be possible to convert
wrapped type to a given target, which is, in my opinion, pretty useless,
unless the real conversion is really heavy
- QMetaType::registerConverter -> allows to register a custom converter
function for a user defined type
- QMetaType::convert -> perform conversion between two types
I would like to agree on some rules, regarding conversions, as the current
approach is chaotic:
1. Are we allowed to add new conversions?
The question is tricky because adding a new conversion is a behavior
change, as this code:
if (variant.canConvert(QMetaType::int)) ...
may work differently. If we add custom types into the mix, everything is
even more complex.
1.1 Patch release or minor release only?
I would say that new conversions may appear only in minor releases,
obvious fixes to existing conversions may appear in patch releases, where by
obvious I mean crash fixes and cases in which the returned value is definitely
bogus.
1.2 Should conversion be perfect or based on a best effort?
Some of the conversion API returns a "bool" value which is a status
of conversion. What should it return if a conversion is not perfect, for
example "int(-1) -> uint" or "QVariantList{string, int, myobject} ->
QStringList", should such a case be detected? How do we define the perfect
conversion? Sometimes only ideal, data lossless, conversions should be
allowed, for example QtTestLib is quite strict about it and it allows only
safe conversions. So, for example, "int -> long" but not "uint -> int", but I
guess for normal use cases such strictness is not necessary.
I think we should base conversions on the best effort and set the
status to false only if a conversion failed completely, that is mainly if a
conversion is unknown or if underlying implementation detected a failure, like
"QByteArray -> float" which uses QByteArray::toFloat(bool *ok)
1.3 Should we try to support a user's type conversions out of the box?
Currently a user needs to manually register a conversion function so
Qt can know it and use it. For certain types we can do much better, because we
can automatically convert some types. For example:
QVector<char> -> QLinkedList<int>
QList<Foo> -> QVector<Foo>
QPointer<Foo> -> QObject*
QPointer<Foo> -> void*
QSharedDataPointer<Foo> -> bool
MyQObject* -> QPointer<MyQObject>
Currently we are not doing it for one reason which is behavior
compatibility. What if a user already defined a conversion that we want to add?
It could happen because the conversion was not available in a previous Qt
version. The problem is that the new conversion function may behave in a
different way, especially in edge cases and because of the lack of perfection
mentioned in 1.2. We need to pick only one function. That could be the Qt
version, but then we risk that an existing code will not work anymore. Are we
willing to accept that?
I believe that we should document the problem, and allow the
conversions.
1.4 Should a user be able to add Qt types conversion on his own?
Some conversions are missing, some we consider as not safe. A user,
assuming that he knows what he is doing, could register a conversion; for
example, "QString -> QChar", how bad is it? Currently, such usage is blocked,
because we are afraid that in the future we may add a conversion that
overrides it.
In my opinion it is not needed; it is a corner case, because we a)
should have the conversion and then it will appear in a future version b) the
conversion is invalid, and it is a sign of a user's broken code.
2. Can we modify an existing conversion?
All modification changes behavior. Of course we assume that changes are
sensible, but still it may break existing code.
2.1 Can we improve a conversion?
For example, "QStringList -> QString" is very tempting, as it works
only if the list is of size 1. The conversion could join all strings instead,
it would be almost backward compatible, as we would alter only conversions
that failed previously.
I believe we should allow that; I can not wait for the bike-shedding
about which character or string we should use to join them.
2.2 How much we can break?
Is missing data in the result of a conversion a reasonable cause to
break behavior? For example "QVariant(QColor) -> QString" doesn't include the
alpha channel (QTBUG-37851).
As I said in 1.2, I think that we should take the approach of the
best effort and nobody should depend on the exact result of a conversion. We
should reserve our right to improve it, so the best effort is not a lie.
2.3 How to decide if a conversion should be improved?
A conversion may be arbitrary. For example, most of the conversions
to QString. Should "bool(false) -> QString" return "False", "0", "false"? What
about precision of, for example, "double -> QString" ?
3. Can we remove existing conversions?
I would say, no, but maybe in a major release.
Cheers,
Jędrek
More information about the Development
mailing list