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 _______________________________________________ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development