From 79abcda47c54c6b273cf6e93d20d18af73ebb0a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnaud=20Cl=C3=A8re?= Date: Sun, 1 Dec 2019 14:57:33 +0100 Subject: [PATCH] Fixed QJsonReader cachelevel More precise null handling including qBindExpectedValue for values that are not null and not among the expecte values (say a QVariant other than list/map and basic types...) --- tests/QBind/QCbor_impl.h | 30 +++++++------- tests/QBind/QJson_impl.h | 47 +++++++++++---------- tests/QBind/QModel_impl.h | 2 +- tests/QBind/QValue.h | 83 +++++++++++++++++++------------------ tests/QBind/QVariant_impl.h | 2 +- tests/QBind/samples.txt | 2 +- 6 files changed, 86 insertions(+), 80 deletions(-) diff --git a/tests/QBind/QCbor_impl.h b/tests/QBind/QCbor_impl.h index 459df3664e..ec0e49b70d 100644 --- a/tests/QBind/QCbor_impl.h +++ b/tests/QBind/QCbor_impl.h @@ -294,10 +294,10 @@ protected: skipTag(); if (isArray () && enterContainer()) { levels.push(Level()); return true; } handleError(qBindExpectedSequence); return false; } bool tryRecord (quint32* s=nullptr) { if (caching) { cacheLevel++; return caching->tryRecord(s); } skipTag(); if (isMap () && enterContainer()) { levels.push(Level()); return true; } handleError(qBindExpectedRecord ); return false; } - bool tryNull ( ) { if (caching) { cacheLevel++; return caching->tryNull() && cacheOut(); } - skipTag(); if (isNull () && next ()) { return true; } handleError(qBindExpectedNull ); return false; } + bool tryNull ( ) { if (caching) { return caching->tryNull() && cacheOut(); } + skipTag(); if ((isNull() || isUndefined()) && next()) { return true; } handleError(qBindExpectedNull); return false; } bool tryBind ( QUtf8Data& u) { QString s; if (tryBind(s)) { u = s.toUtf8(); return true; } return false; } - bool tryBind ( QString& s) { if (caching) { cacheLevel++; return caching->tryBind(s) && cacheOut(); } + bool tryBind ( QString& s) { if (caching) { return caching->tryBind(s) && cacheOut(); } skipTag(); if (isString()) { s.resize(0); auto r = readString(); @@ -313,7 +313,7 @@ protected: } return true; } else { handleError(qBindExpectedText); return false; } } - bool tryBind ( QByteArray& s) { if (caching) { cacheLevel++; return caching->tryBind(s) && cacheOut(); } + bool tryBind ( QByteArray& s) { if (caching) { return caching->tryBind(s) && cacheOut(); } skipTag(); if (isByteArray()) { s.resize(0); auto r = readByteArray(); @@ -329,12 +329,12 @@ protected: } return true; } else { handleError(qBindExpectedBytes); return false; } } - bool tryBind ( bool& b) { if (caching) { cacheLevel++; return caching->tryBind(b) && cacheOut(); } + bool tryBind ( bool& b) { if (caching) { return caching->tryBind(b) && cacheOut(); } skipTag(); if (isBool()) { b=toBool(); return next(); } else { handleError(qBindExpectedBoolean); return false; } } - bool tryBind ( double& n) { if (caching) { cacheLevel++; return caching->tryBind(n) && cacheOut(); } + bool tryBind ( double& n) { if (caching) { return caching->tryBind(n) && cacheOut(); } return getNumber(n); } bool tryBind ( float& n) { double d; if (!tryBind(d)) { return false; } @@ -342,12 +342,12 @@ protected: double(std::numeric_limits::max()) tryBind(t) && cacheOut(); } + bool tryBind ( quint64& t) { if (caching) { return caching->tryBind(t) && cacheOut(); } quint64 i; bool neg; if (!getInteger(i,neg)) { handleError(qBindExpectedInteger ); return false; } if ( neg ) { handleError(qBindExpectedPositiveInteger); return false; } t=i; return true; } - bool tryBind ( qint64& t) { if (caching) { cacheLevel++; return caching->tryBind(t) && cacheOut(); } + bool tryBind ( qint64& t) { if (caching) { return caching->tryBind(t) && cacheOut(); } quint64 i; bool neg; if (!getInteger(i,neg)) { handleError(qBindExpectedInteger ); return false; } if ( neg ) { if (quint64(-std::numeric_limits::min())tryOut() && cacheOut(); } + bool tryOut ( ) { if (caching) { bool out = caching->tryOut(); if (out) { --cacheLevel; cacheOut(); } return out; } levels.pop(); while (hasNext()) { tryAny(); @@ -433,9 +433,8 @@ private: handleError(qBindExpectedInteger); return false; } - bool cacheOut() { Q_ASSERT(cacheLevel); - if (!--cacheLevel) { - caching=nullptr; + bool cacheOut() { if (!cacheLevel) { + caching = nullptr; if (!device()->isSequential()) { device()->seek(cachingAfter); } @@ -471,7 +470,8 @@ struct QTransmogrifier { if (j.isDouble ()) return v.bind(j.toDouble ()); if (j.isString ()) return v.bind(j.toString ()); if (j.isByteArray()) return v.bind(j.toByteArray()); - return v.null(); + if (j.isNull() || j.isUndefined() || j.isInvalid() || v->handleError(qBindExpectedValue)) return v.null(); + return QValueStatus(); } else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.null(); } } @@ -491,9 +491,9 @@ struct QTransmogrifier { j = QCborValue(double(u)); v->setErrorHandler(suspended); return r; } double d; if ((r = v.bind(d))) { j = QCborValue( d ); v->setErrorHandler(suspended); return r; } QByteArray y; if ((r = v.bind(y))) { j = QCborValue( y ); v->setErrorHandler(suspended); return r; } - /**/ if ((r = v.null( ))) { j = QCborValue( ); v->setErrorHandler(suspended); return r; } + r = v.null(); v->setErrorHandler(suspended); - v->handleError(qBindIgnoredItem); + if (r || v->handleError(qBindExpectedValue)) { j = QCborValue(); } return r; } else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.null(); } diff --git a/tests/QBind/QJson_impl.h b/tests/QBind/QJson_impl.h index 08dc69b9f6..d9c10bd91e 100644 --- a/tests/QBind/QJson_impl.h +++ b/tests/QBind/QJson_impl.h @@ -120,7 +120,7 @@ public: protected: bool trySequence(quint32* =nullptr) { if (current().isArray ()) { steps.push(Step()); return true; } handleError(qBindExpectedSequence); return false; } bool tryRecord (quint32* =nullptr) { if (current().isObject()) { steps.push(Step()); return true; } handleError(qBindExpectedRecord ); return false; } - bool tryNull ( ) { if (current().isNull ()) { return true; } handleError(qBindExpectedNull ); return false; } + bool tryNull ( ) { if (current().isNull() || current().isUndefined()) { return true; } handleError(qBindExpectedNull ); return false; } bool tryBind ( QUtf8Data& u) { QString s; if (tryBind(s) ) { u = s .toUtf8 (); return true; } return false; } bool tryBind ( QString& v) { if (current().isString()) { v = current().toString(); return true; } handleError(qBindExpectedText ); return false; } bool tryBind ( bool& v) { if (current().isBool ()) { v = current().toBool (); return true; } handleError(qBindExpectedBoolean ); return false; } @@ -251,14 +251,14 @@ protected: if (get('[', "[{\"ntf-0123456789.")) { levels.push(Step(-1,"]")); return true; } handleError(qBindExpectedSequence); return false; } bool tryRecord (quint32* s=nullptr) { if (caching) { cacheLevel++; return caching->tryRecord(s); } if (get('{', "[{\"ntf-0123456789.")) { levels.push(Step(-1,"}")); return true; } handleError(qBindExpectedRecord ); return false; } - bool tryNull ( ) { if (caching) { cacheLevel++; return caching->tryNull() && cacheOut(); } + bool tryNull ( ) { if (caching) { return caching->tryNull() && cacheOut(); } if (get('n', "[{\"ntf-0123456789.") && - get('u', "[{\"" ) && - get('l', "[{\"" ) && - get('l', "[{\"" )) { + get('u', ",}]") && + get('l', ",}]") && + get('l', ",}]")) { return true; } else { handleError(qBindExpectedNull); return false; } } - bool tryBind ( QUtf8Data& s) { if (caching) { cacheLevel++; return caching->tryBind(s) && cacheOut(); } + bool tryBind ( QUtf8Data& s) { if (caching) { return caching->tryBind(s) && cacheOut(); } QByteArray u; if ( get('"', "[{\"ntf-0123456789.")) { u.resize(0); char c; while ((c=getCharInString()) != '\0' ) { u.append(c); } @@ -266,7 +266,7 @@ protected: return get('"'); } else { handleError(qBindExpectedText); return false; } } bool tryBind ( QString& s) { QUtf8Data u; if (tryBind(u)) { s = QString(u.utf8()); return true; } return false; } - bool tryBind ( bool& b) { if (caching) { cacheLevel++; return caching->tryBind(bool(b)) && cacheOut(); } + bool tryBind ( bool& b) { if (caching) { return caching->tryBind(b) && cacheOut(); } if (get('t', "[{\"ntf-0123456789.") && get('r', "[{\"" ) && get('u', "[{\"" ) && @@ -280,28 +280,28 @@ protected: get('e', "[{\"" )) { b=false; return true; } else { handleError(qBindExpectedBoolean); return false; } } - bool tryBind ( float& n) { if (caching) { cacheLevel++; return caching->tryBind(double(n)) && cacheOut(); } + bool tryBind ( float& n) { if (caching) { return caching->tryBind(n) && cacheOut(); } if (getNumber()==None) return false; // already reported qBindExpectedDecimal if (d::min())|| double( std::numeric_limits::max())tryBind(double(n)) && cacheOut(); } + bool tryBind ( double& n) { if (caching) { return caching->tryBind(n) && cacheOut(); } if (getNumber()==None) return false; // already reported qBindExpectedDecimal n = d ; cachedNumber=None; return true; } - bool tryBind ( quint64& t) { if (caching) { cacheLevel++; return caching->tryBind(double(t)) && cacheOut(); } + bool tryBind ( quint64& t) { if (caching) { return caching->tryBind(t) && cacheOut(); } auto r=getNumber(); if (r==None) return false; // already reported qBindExpectedDecimal if (r==FloatingPoint) { handleError(qBindExpectedInteger); return false; } if (neg) { handleError(qBindExpectedPositiveInteger); return false; } t = i ; cachedNumber=None; return true; } - bool tryBind ( qint64& t) { if (caching) { cacheLevel++; return caching->tryBind(double(t)) && cacheOut(); } + bool tryBind ( qint64& t) { if (caching) { return caching->tryBind(t) && cacheOut(); } auto r=getNumber(); if (r==None) return false; // already reported qBindExpectedDecimal if (r==FloatingPoint) { handleError(qBindExpectedInteger); return false; } if (!neg && i::max())) { t = qint64(i); cachedNumber=None; return true; } if ( neg && i::min())) { t = -qint64(i); cachedNumber=None; return true; } - handleError(qBindExpectedSmallerNumber ); return false; } - bool tryBind ( QVariant& dst) { + handleError(qBindExpectedSmallerNumber); return false; } + bool tryBind ( QVariant& dst) { if (caching) { return caching->tryBind(dst) && cacheOut(); } auto suspended = setErrorHandler(); quint32 size=0; QIdentifier key; QVariant item; @@ -317,11 +317,11 @@ protected: dst = QVariant(QString::fromUtf8(s.utf8())) ; return true; } setErrorHandler(suspended); - if (!tryNull()) handleError(QIdentifierLiteral("ExpectedOneOfBooleanDecimalBytesTextSequenceRecordNull")); + if (!tryNull()) handleError(qBindExpectedValue); dst = QVariant(); return true; } - bool tryOut ( ) { if (caching) { return caching->tryOut() && cacheOut(); } + bool tryOut ( ) { if (caching) { bool out = caching->tryOut(); if (out) { cacheLevel--; cacheOut(); } return out; } auto level = levels.pop(); while (get(',', level.end)) { tryAny(); @@ -372,7 +372,7 @@ protected: return true; } bool isValid() const noexcept { return io; } - bool handleError(QIdentifierLiteral e, QString context = QString()) { return errorHandler ? errorHandler(e, QString("at (%1,%2) ").arg(line).arg(column).append(context)) : false; } + bool handleError(QIdentifierLiteral e, QString context = QString()) { return errorHandler ? errorHandler(e, QString("(%1:%2)").arg(line).arg(column).append(context)) : false; } private: CachedNumber getNumber() { if (cachedNumber!=None) return cachedNumber; @@ -492,15 +492,16 @@ private: CachedNumber cachedNumber = None; double d; quint64 i; bool neg; - bool cacheOut() { Q_ASSERT(cacheLevel); - if (!--cacheLevel) { caching=nullptr; } + bool cacheOut() { if (!cacheLevel) { + caching = nullptr; + } return true; } // Read/Write caching of out-of-order item keys quint8 cacheLevel = 0; QJsonValue cachedValue ; //!< Only used AFTER --cacheLevel==1 QJsonBuilder cacheWriter ; - QAbstractValue* caching = nullptr; //!< Only used when cacheLevel>=1 && key!=expectedKey + QAbstractValue* caching = nullptr; //!< Only used when cacheLevel>=1 && key!=expectedKey QJsonVisitor cacheReader ; //!< Only used as caching }; @@ -520,7 +521,8 @@ struct QTransmogrifier { if (j.isBool ()) return v.bind(j.toBool ()); if (j.isDouble()) return v.bind(j.toDouble()); if (j.isString()) return v.bind(j.toString()); - return v.null(); + if (j.isNull() || j.isUndefined() || v->handleError(qBindExpectedValue)) return v.null(); + return QValueStatus(); } else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.null(); } } @@ -538,9 +540,10 @@ struct QTransmogrifier { qint64 i; if ((r = v.bind(i))) { j = QJsonValue(double(i)); return r; } quint64 u; if ((r = v.bind(u))) { j = QJsonValue(double(u)); return r; } double d; if ((r = v.bind(d))) { j = QJsonValue( d ); return r; } + r = v.null(); v->setErrorHandler(suspended); - if (!(r = v.null())) r.handleError(qBindExpectedValue); - /**/ j = QJsonValue( ); return r; + if (r || v->handleError(qBindExpectedValue)) { j = QJsonValue(); }; + return r; } else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.null(); } } diff --git a/tests/QBind/QModel_impl.h b/tests/QBind/QModel_impl.h index 592a7360b7..d53271a5ff 100644 --- a/tests/QBind/QModel_impl.h +++ b/tests/QBind/QModel_impl.h @@ -595,7 +595,7 @@ protected: using QAbstractValue::tryBind; // full overload set so that following overloads calls work (instead of calling themselves and overflowing the call stack) - virtual bool tryNull( ) { return hidden() ? true : I<=d ? itemBind()->tryNull() : read().isNull () ; } + virtual bool tryNull( ) { return hidden() ? true : I<=d ? itemBind()->tryNull() : read().isNull () || !read().isValid(); } virtual bool tryBind( QString&& u) { return hidden() ? true : I<=d ? itemBind()->tryBind(std::move(u)) : u==read().toString() ; } virtual bool tryBind( bool&& b) { return hidden() ? true : I<=d ? itemBind()->tryBind(std::move(b)) : b==read().toBool () ; } virtual bool tryBind( float&& f) { return hidden() ? true : I<=d ? itemBind()->tryBind(std::move(f)) : qFuzzyCompare(f,read().toFloat ()); } diff --git a/tests/QBind/QValue.h b/tests/QBind/QValue.h index 870db0647d..1d73d30e6a 100644 --- a/tests/QBind/QValue.h +++ b/tests/QBind/QValue.h @@ -348,12 +348,12 @@ public: /**/ T_ bind ( QLatin1String l) { return outer->tryBind (l) ? std::move(outer) : T_ (); } /**/ T_ bind ( QStringView u) { return outer->tryBind (u) ? std::move(outer) : T_ (); } - template T_ bind ( T&& t) { return outer->tryBind(std::forward (t )) ? std::move(outer) : T_(); } - template T_ bind (T& t, T&& defaultT) { return outer->tryBind(QDefaultValue{t,defaultT}) ? std::move(outer) : T_(); } + template T_ bind( T&& t) { return outer->tryBind(std::forward (t )) ? std::move(outer) : T_(); } + template T_ bind(T& t, T&& defaultT) { return outer->tryBind(QDefaultValue{t,defaultT}) ? std::move(outer) : T_(); } // Custom bind support - /**/ T_ with ( QValLambda customBind) { return customBind( std::move(unsafeThis())) ? std::move(outer) : T_(); } - template T_ with (T& t, QValFunction customBind) { return customBind(t, std::move(unsafeThis())) ? std::move(outer) : T_(); } + /**/ T_ with( QValLambda customBind) { return customBind( std::move(unsafeThis())) ? std::move(outer) : T_(); } + template T_ with(T& t, QValFunction customBind) { return customBind(t, std::move(unsafeThis())) ? std::move(outer) : T_(); } // Literal metadata support QVal meta(const char* n, const char* m) { return meta(QIdentifierLiteral(n),QAsciiData(m)); } @@ -385,7 +385,7 @@ public: QValueStatus* operator->() noexcept { return outer.operator ->(); } operator QValueStatus() { return out(); /* calls T_::operator QValueStatus() if T_ != QValueStatus */ } - /**/ T_ out() { return outer-> tryOut() ? std::move(outer) : T_ (); } + /**/ T_ out() { return outer-> tryOut() ? std::move(outer) : T_ (); } QVal> item() { return outer->tryItem() ? QVal>(std::move(*this)) : QVal>(); } // Shortcuts @@ -574,7 +574,6 @@ struct QAbstractValueWriter : public QAbstractValue } return tryOut(); } - if (src.isNull() ) return tryNull(); if (src.userType() // TODO src.type if QUtf8Data becomes a Qt type ==qMetaTypeId()) return tryBind(src.value< QUtf8Data>()); if (src.type()==QVariant::String ) return tryBind(src.value< QString>()); @@ -605,7 +604,8 @@ struct QAbstractValueWriter : public QAbstractValue return tryBind(binaryVariant); } - return tryNull(); + if (src.isNull() || !src.isValid() || handleError(qBindExpectedValue)) return tryNull(); + return false; } virtual bool tryBind( QUtf8Data& r) { QUtf8Data copy(r); return tryBind(std::move(copy)); } @@ -846,7 +846,7 @@ QSeq QSeq::forEach(Ts& ts, // For Q_DEFINE_ZAP_WITH_METAOBJECT below template -QValueStatus qzap(QValue&& v, T* t) { +QValueStatus qMetaZap(QValue&& v, T* t) { auto rw = v->mode(); auto mo = T::staticMetaObject; auto r = v.meta(qmName,QAsciiData(mo.className())).record(); @@ -876,17 +876,16 @@ QValueStatus qzap(QValue&& v, T* t) { if (!pv.isValid() && !v->handleError(qBindIgnoredItem)) { return QValueStatus(); } - else if (!pv.isNull()) { + else { QIdentifier id(p.name()); auto i = r.item(id); - if (p.isEnumType()) { + if (pv.isValid() && p.isEnumType()) { pv.convert(QVariant::Int); i = i.meta(qmName, p.isFlagType() ? QAsciiData(p.enumerator().valueToKeys(pv.value())) : QAsciiData(p.enumerator().valueToKey (pv.value()))); } - if (pv.isNull() ) r = i.null(); - else if (pv.userType()==qMetaTypeId() // TODO pv.type if QUtf8Data becomes a Qt type + if (pv.userType()==qMetaTypeId() // TODO pv.type if QUtf8Data becomes a Qt type ) r = i.bind(pv.value< QUtf8Data>()); else if (pv.type()==QVariant::String ) r = i.bind(pv.value< QString>()); else if (pv.type()==QVariant::Char ) r = i.bind(pv. toString()); @@ -907,7 +906,7 @@ QValueStatus qzap(QValue&& v, T* t) { // See QT_FOR_EACH_STATIC_PRIMITIVE_TYPE in qmetatype.h else if (pv.canConvert() ) r = i.bind(pv.value()); // TODO avoid interim data structure else if (pv.canConvert() ) r = i.bind(pv.value()); // TODO avoid interim data structure - else r = i.bind(pv); // FIXME remove one QVariant level as in QAbstractValueWriter::tryBind(QVariant&&) + else if (pv.isNull() || (!pv.isValid() && v->handleError(qBindExpectedValue))) r = i.null(); } } else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.null(); } @@ -917,21 +916,23 @@ QValueStatus qzap(QValue&& v, T* t) { } //! Default bind(QValue&&) based on static QMetaObject reflection -#define Q_DEFINE_ZAP_WITH_METAOBJECT(Class) QValueStatus zap(QValue&& v) { return qzap(std::move(v), this); } +#define Q_DEFINE_ZAP_WITH_METAOBJECT(Class) QValueStatus zap(QValue&& v) { return qMetaZap(std::move(v), this); } template struct QTransmogrifier> { static QValueStatus zap(QValue&& v, QDefaultValue&& t) { - if (v->mode()==Read) { + if (v->mode()!=Read) { + return v.bind(t.value); + } else { auto suspended = v->setErrorHandler(); - auto r=v.null(); + auto r = v.bind(t.value); v->setErrorHandler(suspended); - if (r) { + if (!r) { t.value = t.defaultValue; - return r; + return v.null(); } + return r; } - return v.bind(t.value); } static QValueStatus zap(QValue&& v, QDefaultValue& t) { return bind(std::move(v),std::move(t)); @@ -1165,29 +1166,31 @@ struct QTransmogrifier { /**/ dstRec.out(); return srcRec.out(); } - if ( (srcRes = src.null())) { - return dst.null(); - } - QUtf8Data u; if ((srcRes = src.bind( u))) { dst.bind( u); return srcRes; } //! \remark QUtf8Data has common valus with QString but is more precise and efficient - QString t; if ((srcRes = src.bind( t))) { dst.bind( t); return srcRes; } + QUtf8Data u; if ((srcRes = src.bind( u))) { dst.bind( u); src->setErrorHandler(suspended); return srcRes; } //! \remark QUtf8Data has common valus with QString but is more precise and efficient + QString t; if ((srcRes = src.bind( t))) { dst.bind( t); src->setErrorHandler(suspended); return srcRes; } // Explicitely handled types (not value text representation convertible to those types - QByteArray a; if ((srcRes = src.bind( a))) { dst.bind( a); return srcRes; } - bool b; if ((srcRes = src.bind( b))) { dst.bind( b); return srcRes; } - - quint8 u8; if ((srcRes = src.bind( u8))) { dst.bind( u8); return srcRes; } //! \remark quint8 has common values with qint8 but is preferred for convenience and static guarantees (no negative values) - qint8 l8; if ((srcRes = src.bind( l8))) { dst.bind( l8); return srcRes; } - quint16 u16; if ((srcRes = src.bind(u16))) { dst.bind(u16); return srcRes; } //! \remark Bigger numeric types can only be handled if QAbstractValue refuses to lose sign or leading digits - qint16 l16; if ((srcRes = src.bind(l16))) { dst.bind(l16); return srcRes; } - quint32 u32; if ((srcRes = src.bind(u32))) { dst.bind(u32); return srcRes; } - qint32 l32; if ((srcRes = src.bind(l32))) { dst.bind(l32); return srcRes; } - - quint64 u64; if ((srcRes = src.bind(u64))) { dst.bind(u64); return srcRes; } - qint64 l64; if ((srcRes = src.bind(l64))) { dst.bind(l64); return srcRes; } - float f; if ((srcRes = src.bind( f))) { dst.bind( f); return srcRes; } //! \remark Floating point numeric types can only be handled if QAbstractValue refuses to lose non zero decimals - double d; if ((srcRes = src.bind( d))) { dst.bind( d); return srcRes; } //! \remark Double-precision floating point numeric types can only be handled if QAbstractValue refuses to lose leading digits or precision + QByteArray a; if ((srcRes = src.bind( a))) { dst.bind( a); src->setErrorHandler(suspended); return srcRes; } + bool b; if ((srcRes = src.bind( b))) { dst.bind( b); src->setErrorHandler(suspended); return srcRes; } + + quint8 u8; if ((srcRes = src.bind( u8))) { dst.bind( u8); src->setErrorHandler(suspended); return srcRes; } //! \remark quint8 has common values with qint8 but is preferred for convenience and static guarantees (no negative values) + qint8 l8; if ((srcRes = src.bind( l8))) { dst.bind( l8); src->setErrorHandler(suspended); return srcRes; } + quint16 u16; if ((srcRes = src.bind(u16))) { dst.bind(u16); src->setErrorHandler(suspended); return srcRes; } //! \remark Bigger numeric types can only be handled if QAbstractValue refuses to lose sign or leading digits + qint16 l16; if ((srcRes = src.bind(l16))) { dst.bind(l16); src->setErrorHandler(suspended); return srcRes; } + quint32 u32; if ((srcRes = src.bind(u32))) { dst.bind(u32); src->setErrorHandler(suspended); return srcRes; } + qint32 l32; if ((srcRes = src.bind(l32))) { dst.bind(l32); src->setErrorHandler(suspended); return srcRes; } + + quint64 u64; if ((srcRes = src.bind(u64))) { dst.bind(u64); src->setErrorHandler(suspended); return srcRes; } + qint64 l64; if ((srcRes = src.bind(l64))) { dst.bind(l64); src->setErrorHandler(suspended); return srcRes; } + float f; if ((srcRes = src.bind( f))) { dst.bind( f); src->setErrorHandler(suspended); return srcRes; } //! \remark Floating point numeric types can only be handled if QAbstractValue refuses to lose non zero decimals + double d; if ((srcRes = src.bind( d))) { dst.bind( d); src->setErrorHandler(suspended); return srcRes; } //! \remark Double-precision floating point numeric types can only be handled if QAbstractValue refuses to lose leading digits or precision // TODO Other BindNative types we do not want to treat as text: QDateTime, QUuid - src->setErrorHandler(suspended); - srcRes.handleError(qBindExpectedValue); + srcRes = src.null(); + if (!srcRes) { + src->setErrorHandler(suspended); + if (src->handleError(qBindExpectedValue)) { dst.null(); } + return QValueStatus(); + } + srcRes->setErrorHandler(suspended); return srcRes; } else if (src->mode()==Write && dst->mode()==Read) { return zap(std::move(dst),std::move(src)); } // To return reader's QValueStatus instead of writer's QValueStatus since it should contain more information (mismatches, etc.) diff --git a/tests/QBind/QVariant_impl.h b/tests/QBind/QVariant_impl.h index 7be9c8b0b9..fe56638363 100644 --- a/tests/QBind/QVariant_impl.h +++ b/tests/QBind/QVariant_impl.h @@ -168,7 +168,7 @@ protected: bool trySequence(quint32* =nullptr) { if (current()->type()==QVariant::List ) { levels.push(Level()); return true; } handleError(qBindExpectedSequence); return false; } bool tryRecord (quint32* =nullptr) { if (current()->type()==QVariant::Map ) { levels.push(Level()); return true; } handleError(qBindExpectedRecord ); return false; } - bool tryNull ( ) { if (current()->isNull() ) { return true; } handleError(qBindExpectedNull ); return false; } + bool tryNull ( ) { if (current()->isNull() || !current()->isValid()) { return true; } handleError(qBindExpectedNull ); return false; } bool tryItem(QIdentifier& k) { levels.last().key=k; return (levels.last().item = current(1)->toMap ().value(QString(levels.last().key.latin1()), QVariant())).isValid(); } bool tryItem( ) { levels.last().idx++; return (levels.last().item = current(1)->toList().value( levels.last().idx , QVariant())).isValid(); } diff --git a/tests/QBind/samples.txt b/tests/QBind/samples.txt index 168c099235..20c2e812eb 100644 --- a/tests/QBind/samples.txt +++ b/tests/QBind/samples.txt @@ -57,7 +57,7 @@ Bindable | Bindable>Cbor |bf647479706502666e756d6265726b2b34342031323334353637ff Bindable>Json |{"type":2,"number":"+44 1234567"} Person<>Json |================================================================================ -Json>P |(Person) names:[ John Doe] height:1.75 age:-1 phones:[] comments: children:[] | at (0,1) IgnoredCharacter, at (0,15) IgnoredCharacter, at (0,23) IgnoredCharacter, at (0,40) IgnoredCharacter +Json>P |(Person) names:[ John Doe] height:1.75 age:-1 phones:[] comments: children:[] | (0:1)IgnoredCharacter, (0:15)IgnoredCharacter, (0:23)IgnoredCharacter, (0:40)IgnoredCharacter, (0:58)IgnoredCharacter, (0:102)IgnoredCharacter P>Json |{"names":["John","Doe"],"height":1.7500000000000002,"age":-1,"phones":[],"comments":"","children":[]} Json>P |(Person) names:[ John Doe] height:1.75 age:-1 phones:[] comments: children:[] | P>JsonValue |{"age":-1,"children":[],"comments":"","height":1.7500000000000004,"names":["John","Doe"],"phones":[]} -- GitLab