diff --git a/tests/QBind/QCbor_impl.h b/tests/QBind/QCbor_impl.h index f1500974765d4a7dd450e01476e090156a0faec1..c0b52a0106ab9b9806354c39693f178c33ec0cca 100644 --- a/tests/QBind/QCbor_impl.h +++ b/tests/QBind/QCbor_impl.h @@ -198,7 +198,7 @@ protected: bool tryBind ( bool&& b) { set(QCborValue( b )); return true; } bool tryBind ( float&& d) { set(QCborValue(double(d))); return true; } bool tryBind ( double&& d) { set(QCborValue( d )); return true; } - bool tryBind ( quint64&& n) { if (quint64(std::numeric_limits::max())::max()) errors; + void reset(QCborValue* v) { cbor=v; Q_ASSERT(v); steps.resize(0); } QAsciiData currentPath() { QByteArray path; @@ -250,17 +247,17 @@ public: /**/ QSequence sequence(quint32* s=nullptr) { return QValueStatus(this).value().sequence(s); } template QValueStatus bind ( T&& t) { return QValueStatus(this).value().bind(std::forward(t)); } protected: - bool trySequence(quint32* =nullptr) { if (current().isArray ()) { steps.push(Step()); return true; } _reportError(qBindExpectedSequence); return false; } - bool tryRecord (quint32* =nullptr) { if (current().isMap ()) { steps.push(Step()); return true; } _reportError(qBindExpectedRecord ); return false; } - bool tryNull ( ) { if (current().isNull ()) { return true; } _reportError(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; } _reportError(qBindExpectedText ); return false; } - bool tryBind ( bool& v) { if (current().isBool ()) { v = current().toBool (); return true; } _reportError(qBindExpectedBoolean ); return false; } - bool tryBind ( qint64& t) { if (current().isInteger ()) { t = current().toInteger (); return true; } _reportError(qBindExpectedInteger ); return false; } - bool tryBind ( quint64& t) { qint64 i; if (tryBind(i) ) { t = quint64(i) ; return true; } return false; } - bool tryBind ( float& v) { double d; if (tryBind(d) ) { v = float(d) ; return true; } return false; } - bool tryBind ( double& v) { if (current().isDouble ()) { v = current().toDouble (); return true; } _reportError(qBindExpectedDecimal ); return false; } - bool tryBind ( QByteArray& v) { QString s; if (current().isByteArray()) { v = current().toByteArray(); return true; } _reportError(qBindExpectedBytes ); return false; } + bool trySequence(quint32* =nullptr) { if (current().isArray ()) { steps.push(Step()); return true; } reportError(qBindExpectedSequence); return false; } + bool tryRecord (quint32* =nullptr) { if (current().isMap ()) { steps.push(Step()); return true; } reportError(qBindExpectedRecord ); return false; } + bool tryNull ( ) { if (current().isNull ()) { return true; } reportError(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; } reportError(qBindExpectedText ); return false; } + bool tryBind ( bool& v) { if (current().isBool ()) { v = current().toBool (); return true; } reportError(qBindExpectedBoolean ); return false; } + bool tryBind ( qint64& t) { if (current().isInteger ()) { t = current().toInteger (); return true; } reportError(qBindExpectedInteger ); return false; } + bool tryBind ( quint64& t) { qint64 i; if (tryBind(i) ) { t = quint64(i) ; return true; } return false; } + bool tryBind ( float& v) { double d; if (tryBind(d) ) { v = float(d) ; return true; } return false; } + bool tryBind ( double& v) { if (current().isDouble ()) { v = current().toDouble (); return true; } reportError(qBindExpectedDecimal ); return false; } + bool tryBind ( QByteArray& v) { QString s; if (current().isByteArray()) { v = current().toByteArray(); return true; } reportError(qBindExpectedBytes ); return false; } bool tryItem(QIdentifierLiteral u) { steps.last().key=u; return !(steps.last().item = current(1).toMap().value(steps.last().key.latin1())).isUndefined(); } bool tryItem(QIdentifier& k) { steps.last().key=k; return !(steps.last().item = current(1).toMap().value(steps.last().key.latin1())).isUndefined(); } @@ -268,15 +265,13 @@ protected: bool tryOut ( ) { steps.pop() ; return true; } bool _isOk() const noexcept { return cbor; } - void _setChoice(bool v) { isChoice=v; } - void _reportError(QIdentifierLiteral e) { if (!isChoice) errors.append(Error{ e, currentPath() }); } + void reportError(QIdentifierLiteral e, QString context = QString()) { if (errorHandler) errorHandler(e, QString(currentPath().latin1()).append(context)); } private: const QCborValue& current(int outer=0) const { Q_ASSERT(0<=outer); return steps.size()-outer <= 0 ? *cbor : steps[steps.size()-outer-1].item; } QCborValue* cbor; struct Step { QIdentifier key; int idx=-1; QCborValue item; Step() = default; }; QStack steps = QStack(); - bool isChoice = false; }; // -------------------------------------------------------------------------- @@ -290,20 +285,17 @@ class QCborReader : public QAbstractValueReader, public QCborStreamReader public: QCborReader(QIODevice* io) : QCborStreamReader(io), cacheVisitor(&cachedValue) { Q_ASSERT(io); } - struct Error { QIdentifierLiteral error; qint64 index; QCborError cborError; QValueStatus zap(QValue&& value) { QByteArray utf8(error.utf8()); utf8.append(' ').append(QByteArray::number(index)); return value.bind(utf8.constData()); } }; - QVector errors; - // Shortcuts /**/ QValue value ( ) { return QValueStatus(this).value(); } /**/ QSequence sequence(quint32* s=nullptr) { return QValueStatus(this).value().sequence(s); } template QValueStatus bind ( T&& t) { return QValueStatus(this).value().bind(std::forward(t)); } protected: bool trySequence(quint32* s=nullptr) { if (caching) { cacheLevel++; return caching->trySequence(s); } - skipTag(); if (isArray () && enterContainer()) { levels.push(Level()); return true; } _reportError(qBindExpectedSequence); return false; } + skipTag(); if (isArray () && enterContainer()) { levels.push(Level()); return true; } reportError(qBindExpectedSequence); return false; } bool tryRecord (quint32* s=nullptr) { if (caching) { cacheLevel++; return caching->tryRecord(s); } - skipTag(); if (isMap () && enterContainer()) { levels.push(Level()); return true; } _reportError(qBindExpectedRecord ); return false; } + skipTag(); if (isMap () && enterContainer()) { levels.push(Level()); return true; } reportError(qBindExpectedRecord ); return false; } bool tryNull ( ) { if (caching) { cacheLevel++; return caching->tryNull() && cacheOut(); } - skipTag(); if (isNull () && next ()) { return true; } _reportError(qBindExpectedNull ); return false; } + skipTag(); if (isNull () && next ()) { return true; } reportError(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(); } skipTag(); if (isString()) { @@ -315,13 +307,13 @@ protected: } if (r.status == QCborStreamReader::Error) { if (!s.isEmpty()) { - _reportError(qBindIgnoredBytes); + reportError(qBindIgnoredBytes); s.resize(0); } return false; } return true; - } else { _reportError(qBindExpectedBytes); return false; } } + } else { reportError(qBindExpectedBytes); return false; } } bool tryBind ( QByteArray& s) { if (caching) { cacheLevel++; return caching->tryBind(s) && cacheOut(); } skipTag(); if (isByteArray()) { s.resize(0); @@ -332,36 +324,36 @@ protected: } if (r.status == QCborStreamReader::Error) { if (!s.isEmpty()) { - _reportError(qBindIgnoredBytes); + reportError(qBindIgnoredBytes); s.resize(0); } return false; } return true; - } else { _reportError(qBindExpectedBytes); return false; } } + } else { reportError(qBindExpectedBytes); return false; } } bool tryBind ( bool& b) { if (caching) { cacheLevel++; return caching->tryBind(b) && cacheOut(); } skipTag(); if (isBool()) { b=toBool(); return next(); - } else { _reportError(qBindExpectedBoolean); return false; } } + } else { reportError(qBindExpectedBoolean); return false; } } bool tryBind ( double& n) { if (caching) { cacheLevel++; return caching->tryBind(n) && cacheOut(); } return getNumber(n); } bool tryBind ( float& n) { double d; if (!tryBind(d)) { return false; } if (d::min()) || - double(std::numeric_limits::max()) ::max()) tryBind(t) && cacheOut(); } quint64 i; bool neg; - if (!getInteger(i,neg)) { _reportError(qBindExpectedInteger ); return false; } - if ( neg ) { _reportError(qBindExpectedPositiveInteger); return false; } + if (!getInteger(i,neg)) { reportError(qBindExpectedInteger ); return false; } + if ( neg ) { reportError(qBindExpectedPositiveInteger); return false; } t=i; return true; } bool tryBind ( qint64& t) { if (caching) { cacheLevel++; return caching->tryBind(t) && cacheOut(); } quint64 i; bool neg; - if (!getInteger(i,neg)) { _reportError(qBindExpectedInteger ); return false; } - if ( neg ) { if (quint64(-std::numeric_limits::min())::max())::min())::max())tryItem(u); } @@ -428,20 +420,19 @@ protected: bool tryAny() { return next(); } bool _isOk() const noexcept { return const_cast(this)->lastError()==QCborError::NoError; } - void _setChoice(bool b) { isChoice = b; } - void _reportError(QIdentifierLiteral e) { if (!isChoice) errors.append(Error{ e, currentOffset(), lastError() }); } + void reportError(QIdentifierLiteral e, QString context = QString()) { if (errorHandler) errorHandler(e, QString("at index:%1 ").arg(currentOffset()).append(context)); } private: void skipTag() { if (isTag()) next(); } bool getNumber(double& d) { skipTag(); if (isFloat16()) { d = double(toFloat16()); return next(); } if (isFloat ()) { d = double(toFloat ()); return next(); } if (isDouble ()) { d = toDouble () ; return next(); } - _reportError(qBindExpectedDecimal); return false; + reportError(qBindExpectedDecimal); return false; } bool getInteger(quint64&i, bool& isNegative) { skipTag(); if (isNegativeInteger()) { i = quint64(toNegativeInteger()); isNegative = true ; return next(); } if (isUnsignedInteger()) { i = toUnsignedInteger() ; isNegative = false; return next(); } - _reportError(qBindExpectedInteger); return false; + reportError(qBindExpectedInteger); return false; } bool isChoice = false; @@ -494,18 +485,17 @@ struct QTransmogrifier { } else if (v->mode()==Read) { QValueStatus r; - { - QScopedChoice choice(v); - QCborArray a; if ((r = v.bind(a))) { j = a ; return r; } - QCborMap o; if ((r = v.bind(o))) { j = o ; return r; } - QString t; if ((r = v.bind(t))) { j = QCborValue( t ); return r; } - bool b; if ((r = v.bind(b))) { j = QCborValue( b ); return r; } - qint64 i; if ((r = v.bind(i))) { j = QCborValue( i ); return r; } - quint64 u; if ((r = v.bind(u))) { Q_ASSERT(quint64(std::numeric_limits::max())() would have succeeded - j = QCborValue(double(u)); return r; } - double d; if ((r = v.bind(d))) { j = QCborValue( d ); return r; } - QByteArray y; if ((r = v.bind(y))) { j = QCborValue( y ); return r; } - } + auto suspended = v->setErrorHandler(); + QCborArray a; if ((r = v.bind(a))) { j = a ; return r; } + QCborMap o; if ((r = v.bind(o))) { j = o ; return r; } + QString t; if ((r = v.bind(t))) { j = QCborValue( t ); return r; } + bool b; if ((r = v.bind(b))) { j = QCborValue( b ); return r; } + qint64 i; if ((r = v.bind(i))) { j = QCborValue( i ); return r; } + quint64 u; if ((r = v.bind(u))) { Q_ASSERT(quint64(std::numeric_limits::max())() would have succeeded + j = QCborValue(double(u)); return r; } + double d; if ((r = v.bind(d))) { j = QCborValue( d ); return r; } + QByteArray y; if ((r = v.bind(y))) { j = QCborValue( y ); return r; } + v->setErrorHandler(suspended); if (!(r = v.null())) r.reportError(qBindIgnoredItem); /**/ j = QCborValue( ); return r; } diff --git a/tests/QBind/QJson_impl.h b/tests/QBind/QJson_impl.h index e2502f92d3b1113bb629109f9ee069e04367d455..e27615cca7fa7ef1677d15b45fc66a9cc6f3ca6f 100644 --- a/tests/QBind/QJson_impl.h +++ b/tests/QBind/QJson_impl.h @@ -102,10 +102,7 @@ class QJsonVisitor : public QAbstractValueReader Q_DISABLE_COPY(QJsonVisitor) public: QJsonVisitor(const QJsonValue* v) : json(v) { Q_ASSERT(v); } - void reset(QJsonValue* v) { json=v; Q_ASSERT(v); steps.resize(0); errors.resize(0); } - - struct Error { QIdentifierLiteral error; QAsciiData path; QValueStatus zap(QValue&& value) { return value.bind(QUtf8Data(error.utf8()+' '+path.utf8())); } }; - QVector errors; + void reset(QJsonValue* v) { json=v; Q_ASSERT(v); steps.resize(0); } QAsciiData currentPath() { QByteArray path; @@ -122,17 +119,17 @@ public: template QValueStatus bind ( T&& t) { return QValueStatus(this).value().bind(std::forward(t)); } protected: bool trySequence(quint32* =nullptr) { if (current().isArray ()) { steps.push(Step()); - return true; } _reportError(qBindExpectedSequence); return false; } + return true; } reportError(qBindExpectedSequence); return false; } bool tryRecord (quint32* =nullptr) { if (current().isObject()) { steps.push(Step()); - return true; } _reportError(qBindExpectedRecord ); return false; } - bool tryNull ( ) { if (current().isNull ()) { return true; } _reportError(qBindExpectedNull ); return false; } + return true; } reportError(qBindExpectedRecord ); return false; } + bool tryNull ( ) { if (current().isNull ()) { return true; } reportError(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; } _reportError(qBindExpectedText ); return false; } - bool tryBind ( bool& v) { if (current().isBool ()) { v = current().toBool (); return true; } _reportError(qBindExpectedBoolean ); return false; } + bool tryBind ( QString& v) { if (current().isString()) { v = current().toString(); return true; } reportError(qBindExpectedText ); return false; } + bool tryBind ( bool& v) { if (current().isBool ()) { v = current().toBool (); return true; } reportError(qBindExpectedBoolean ); return false; } bool tryBind ( qint64& t) { double d; if (tryBind(d) ) { t = qint64(d) ; return true; } return false; } bool tryBind ( quint64& t) { double d; if (tryBind(d) ) { t = quint64(d) ; return true; } return false; } bool tryBind ( float& v) { double d; if (tryBind(d) ) { v = float(d) ; return true; } return false; } - bool tryBind ( double& v) { if (current().isDouble()) { v = current().toDouble(); return true; } _reportError(qBindExpectedDecimal ); return false; } + bool tryBind ( double& v) { if (current().isDouble()) { v = current().toDouble(); return true; } reportError(qBindExpectedDecimal ); return false; } bool tryItem(QIdentifierLiteral u) { steps.last().key=u; return !(steps.last().item = current(1).toObject().value(steps.last().key.latin1())).isUndefined(); } bool tryItem(QIdentifier& k) { steps.last().key=k; return !(steps.last().item = current(1).toObject().value(steps.last().key.latin1())).isUndefined(); } @@ -140,15 +137,13 @@ protected: bool tryOut ( ) { steps.removeLast(); return true; } bool _isOk() const noexcept { return true; } - void _setChoice(bool v) { isChoice=v; } - void _reportError(QIdentifierLiteral e) { if (!isChoice) errors.append(Error{ e, currentPath() }); } + void reportError(QIdentifierLiteral e, QString context = QString()) { if (errorHandler) errorHandler(e, QString(currentPath().latin1()).append(context)); } private: const QJsonValue& current(int outer=0) const { Q_ASSERT(0<=outer && json); return steps.size()-outer <= 0 ? *json : steps[steps.size()-outer-1].item; } const QJsonValue* json; struct Step { QIdentifier key; int idx=-1; QJsonValue item; Step() = default; }; QStack steps = QStack(); - bool isChoice = false; }; // -------------------------------------------------------------------------- @@ -256,23 +251,23 @@ protected: enum CachedNumber : quint8 { None=0, Integer, FloatingPoint }; bool trySequence(quint32* s=nullptr) { if (caching) { cacheLevel++; return caching->trySequence(s); } - if (get('[', "[{\"ntf-0123456789.")) { levels.push(Step(-1,"]")); return true; } _reportError(qBindExpectedSequence); return false; } + if (get('[', "[{\"ntf-0123456789.")) { levels.push(Step(-1,"]")); return true; } reportError(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; } _reportError(qBindExpectedRecord ); return false; } + if (get('{', "[{\"ntf-0123456789.")) { levels.push(Step(-1,"}")); return true; } reportError(qBindExpectedRecord ); return false; } bool tryNull ( ) { if (caching) { cacheLevel++; return caching->tryNull() && cacheOut(); } if (get('n', "[{\"ntf-0123456789.") && get('u', "[{\"" ) && get('l', "[{\"" ) && get('l', "[{\"" )) { return true; - } else { _reportError(qBindExpectedNull); return false; } } + } else { reportError(qBindExpectedNull); return false; } } bool tryBind ( QUtf8Data& s) { if (caching) { cacheLevel++; return caching->tryBind(s) && cacheOut(); } QByteArray u; if ( get('"', "[{\"ntf-0123456789.")) { u.resize(0); char c; while ((c=getCharInString()) != '\0' ) { u.append(c); } s=QUtf8Data(u); return get('"'); - } else { _reportError(qBindExpectedText); return false; } } + } else { reportError(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(); } if (get('t', "[{\"ntf-0123456789.") && @@ -287,11 +282,11 @@ protected: get('s', "[{\"" ) && get('e', "[{\"" )) { b=false; return true; - } else { _reportError(qBindExpectedBoolean); return false; } } + } else { reportError(qBindExpectedBoolean); return false; } } bool tryBind ( float& n) { if (caching) { cacheLevel++; return caching->tryBind(double(n)) && cacheOut(); } if (getNumber()==None) return false; // already reported qBindExpectedDecimal if (d::min())|| - double( std::numeric_limits::max())::max())tryBind(double(n)) && cacheOut(); } if (getNumber()==None) return false; // already reported qBindExpectedDecimal @@ -299,33 +294,33 @@ protected: bool tryBind ( quint64& t) { if (caching) { cacheLevel++; return caching->tryBind(double(t)) && cacheOut(); } auto r=getNumber(); if (r==None) return false; // already reported qBindExpectedDecimal - if (r==FloatingPoint) { _reportError(qBindExpectedInteger); return false; } - if (neg) { _reportError(qBindExpectedPositiveInteger); return false; } + if (r==FloatingPoint) { reportError(qBindExpectedInteger); return false; } + if (neg) { reportError(qBindExpectedPositiveInteger); return false; } t = i ; cachedNumber=None; return true; } bool tryBind ( qint64& t) { if (caching) { cacheLevel++; return caching->tryBind(double(t)) && cacheOut(); } auto r=getNumber(); if (r==None) return false; // already reported qBindExpectedDecimal - if (r==FloatingPoint) { _reportError(qBindExpectedInteger); return false; } + if (r==FloatingPoint) { reportError(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; } - _reportError(qBindExpectedSmallerNumber ); return false; } + reportError(qBindExpectedSmallerNumber ); return false; } bool tryBind ( QVariant& dst) { - _setChoice(true); + auto suspended = setErrorHandler(); quint32 size=0; QIdentifier key; QVariant item; - if ( trySequence(&size)) { _setChoice(false); QVariantList l; while (tryItem( )) { l.append( tryBind(item) ? item : QVariant()); } dst = l; return tryOut(); } - if ( tryRecord (&size)) { _setChoice(false); QVariantMap l; while (tryItem(key)) { l.insert(key.latin1(), tryBind(item) ? item : QVariant()); } dst = l; return tryOut(); } - bool b; if (tryBind(b)) { _setChoice(false); dst = QVariant(b ); return true; } - quint64 u; if (tryBind(u)) { _setChoice(false); dst = QVariant(u ); return true; } // may fail after consuming integer part - qint64 l; if (tryBind(l)) { _setChoice(false); dst = QVariant(l ); return true; } // may fail after consuming integer part - double d; if (tryBind(d)) { _setChoice(false); dst = QVariant(d+/*integer part consumed by one of*/u+l); return true; } - QUtf8Data s; if (tryBind(s)) { _setChoice(false); + if ( trySequence(&size)) { setErrorHandler(suspended); QVariantList l; while (tryItem( )) { l.append( tryBind(item) ? item : QVariant()); } dst = l; return tryOut(); } + if ( tryRecord (&size)) { setErrorHandler(suspended); QVariantMap l; while (tryItem(key)) { l.insert(key.latin1(), tryBind(item) ? item : QVariant()); } dst = l; return tryOut(); } + bool b; if (tryBind(b)) { setErrorHandler(suspended); dst = QVariant(b ); return true; } + quint64 u; if (tryBind(u)) { setErrorHandler(suspended); dst = QVariant(u ); return true; } // may fail after consuming integer part + qint64 l; if (tryBind(l)) { setErrorHandler(suspended); dst = QVariant(l ); return true; } // may fail after consuming integer part + double d; if (tryBind(d)) { setErrorHandler(suspended); dst = QVariant(d+/*integer part consumed by one of*/u+l); return true; } + QUtf8Data s; if (tryBind(s)) { setErrorHandler(suspended); QByteArray b; if ( toByteArray(b, s)) { toVariant(dst, b); return true; } dst = QVariant(QString::fromUtf8(s.utf8())) ; return true; } - _setChoice(false); - if (!tryNull()) _reportError(QIdentifierLiteral("ExpectedOneOfBooleanDecimalBytesTextSequenceRecordNull")); + setErrorHandler(suspended); + if (!tryNull()) reportError(QIdentifierLiteral("ExpectedOneOfBooleanDecimalBytesTextSequenceRecordNull")); dst = QVariant(); return true; } @@ -380,8 +375,7 @@ protected: return true; } bool _isOk() const noexcept { return io; } - void _setChoice(bool v) { isChoice=v; } - void _reportError(QIdentifierLiteral e) { if (!isChoice) errors.append(Error{ e, line, column, index }); } + void reportError(QIdentifierLiteral e, QString context = QString()) { if (errorHandler) errorHandler(e, QString("at (%1,%2) ").arg(line).arg(column).append(context)); } private: CachedNumber getNumber() { if (cachedNumber!=None) return cachedNumber; @@ -389,7 +383,7 @@ private: neg = get('-', "[{\"ntf-0123456789."); qint8 digit; if ((digit = getDigit()) < 0) { - _reportError(qBindExpectedDecimal); + reportError(qBindExpectedDecimal); return None; // do not accept no digit otherwise we may accept an empty mantissa or string! } cachedNumber=Integer; @@ -484,7 +478,7 @@ private: while (!(nextChar() == expected || strchr(validChars, nextChar()) || nextChar() == '\0')) { char ignored = getChar(); if (!isspace(ignored)) { - errors.append(Error{ qBindIgnoredCharacter, line, column, index }); + reportError(qBindIgnoredCharacter); } } } @@ -538,16 +532,15 @@ struct QTransmogrifier { } else if (v->mode()==Read) { QValueStatus r; - { - QScopedChoice choice(v); - QJsonArray a; if ((r = v.bind(a))) { j = a ; return r; } - QJsonObject o; if ((r = v.bind(o))) { j = o ; return r; } - QString t; if ((r = v.bind(t))) { j = QJsonValue( t) ; return r; } - bool b; if ((r = v.bind(b))) { j = QJsonValue( b) ; return r; } - 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; } - } + auto suspended = v->setErrorHandler(); + QJsonArray a; if ((r = v.bind(a))) { j = a ; return r; } + QJsonObject o; if ((r = v.bind(o))) { j = o ; return r; } + QString t; if ((r = v.bind(t))) { j = QJsonValue( t) ; return r; } + bool b; if ((r = v.bind(b))) { j = QJsonValue( b) ; return r; } + 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; } + v->setErrorHandler(suspended); if (!(r = v.null())) r.reportError("ExpectedBoolDoubleQStringQJsonArrayQJsonOnjectNull"); /**/ j = QJsonValue( ); return r; } diff --git a/tests/QBind/QModel_impl.h b/tests/QBind/QModel_impl.h index 2e0bb8699842f8803ca8dd02a902b6231b5d28c9..91877d85258d9be6c38d994dd527d437595d8f77 100644 --- a/tests/QBind/QModel_impl.h +++ b/tests/QBind/QModel_impl.h @@ -59,9 +59,6 @@ class QModelBind : public QAbstractValue public: QModelBind(QAbstractItemModel* m, bool rowFirst=true) : m(m), rowFirst(rowFirst) { Q_ASSERT(m); } - struct Error { QIdentifierLiteral error; QModelIndex index; QValueStatus zap(QValue&& value) { QByteArray u(error.utf8()); for (; index.isValid(); index=index.parent()) u.append('/').append(QByteArray::number(index.row())).append(',').append(QByteArray::number(index.column())); return value.bind(QUtf8Data(u)); } }; - QVector errors; - QValue value() { return QValueStatus(this).value(); } protected: enum : int { @@ -157,7 +154,15 @@ protected: virtual bool tryBind( QByteArray& r) { return tryBind(QByteArray(r)); } virtual bool tryBind( QVariant& r) { return tryBind( QVariant(r)); } - virtual void _reportError(QIdentifierLiteral n) { errors.append(Error{n, parent}); } + void reportError(QIdentifierLiteral e, QString context = QString()) { + if (errorHandler) { + QString path; + for (auto current = parent; parent.isValid(); current = current.parent()) { + path.prepend(QString('/').append(current.row()).append(',').append(current.column())); + } + errorHandler(e, path.append(context)); + } + } virtual QAbstractValue* itemBind() = 0; @@ -210,6 +215,8 @@ protected: QList columnNames; bool metaColumnNames=false; QVector sizes; + + QValueErrorHandler errorHandler = QValueErrorHandler(); }; // -------------------------------------------------------------------------- @@ -306,7 +313,7 @@ protected: row++; col=0; return true; } - _reportError(qBindIgnoredItem); + reportError(qBindIgnoredItem); return false; } return true; @@ -321,7 +328,7 @@ protected: row=0; col++; return true; } - _reportError(qBindIgnoredItem); + reportError(qBindIgnoredItem); return false; } return true; @@ -352,7 +359,7 @@ protected: col=columnNames.indexOf(n.utf8()); // TODO if (max(dimension())<=col) col=-1; if (col<0) { - _reportError(qBindIgnoredItem); + reportError(qBindIgnoredItem); } } return true; @@ -369,7 +376,7 @@ protected: } else { // TODO if (rowNames... - _reportError(qBindIgnoredItemName); + reportError(qBindIgnoredItemName); return true; } } @@ -502,7 +509,7 @@ protected: if (col < m->columnCount()) { return true; } - _reportError(qBindIgnoredItem); + reportError(qBindIgnoredItem); return false; } return true; @@ -520,7 +527,7 @@ protected: row=0; col++; return true; } - _reportError(qBindIgnoredItem); + reportError(qBindIgnoredItem); return false; } return true; @@ -553,7 +560,7 @@ protected: } // TODO if (max(dimension())<=col) col=-1; if (col<0) { - _reportError(qBindIgnoredItem); + reportError(qBindIgnoredItem); } return true; } @@ -566,7 +573,7 @@ protected: } else if (n!=childrenName) { // TODO if (rowNames... - _reportError(qBindIgnoredItemName); + reportError(qBindIgnoredItemName); return true; } else { return true; } @@ -598,12 +605,12 @@ protected: // TODO QDate*, QTime // TODO QPixmap if metadata suggests a QMimeData image ? - virtual bool tryBind( qint8& n) { qint64 l; if (!tryBind(l)) return false; if (l::min() || std::numeric_limits< qint8>::max()::max()::min() || std::numeric_limits< qint16>::max()::max()::min() || std::numeric_limits< qint32>::max()::max()::min() || std::numeric_limits< qint8>::max()::max()::min() || std::numeric_limits< qint16>::max()::max()::min() || std::numeric_limits< qint32>::max()::max()tryBind(r); QVariant v=read(); if (v.type()!=QVariant ::String ) return false; r=QUtf8Data(v.toString().toUtf8()); return true; } virtual bool tryBind( QString& r) { if (hidden()) return true; if (I<=d) return itemBind()->tryBind(r); QVariant v=read(); if (v.type()!=QVariant ::String ) return false; r= v.toString () ; return true; } diff --git a/tests/QBind/QSettings_impl.h b/tests/QBind/QSettings_impl.h index 933819053c78e26e83e2e6e955bbbff65ba4817b..c2161535b8366fd7358a3878a680cff9ec6718db 100644 --- a/tests/QBind/QSettings_impl.h +++ b/tests/QBind/QSettings_impl.h @@ -96,9 +96,6 @@ public: Q_ENABLE_MOVE(QSettingsReader, std::swap(isChoice, o.isChoice); ) QSettingsReader(QSettings* s) : settings(s) { Q_ASSERT(s); levels.push(Level(qBindExpectedItem)); } - struct Error { QIdentifierLiteral error; QAsciiData path; QValueStatus zap(QValue&& value) { return value.bind(QUtf8Data(error.utf8()+' '+path.utf8())); } }; - QVector errors; - QAsciiData currentPath() { QByteArray path; Q_FOREACH(Level l, levels) { @@ -130,20 +127,19 @@ protected: bool tryBind( float& t) { return set(t, qBindExpectedDecimal ); } bool tryBind( double& t) { return set(t, qBindExpectedDecimal ); } - bool trySequence(quint32* =nullptr) { if (levels.last().isGroup) { levels.push(Level( )); return true; } _reportError(qBindExpectedSequence); return false; } - bool tryRecord (quint32* =nullptr) { if (levels.last().isGroup) { levels.push(Level(qBindExpectedItem)); return true; } _reportError(qBindExpectedRecord ); return false; } - bool tryNull ( ) { if (settings->value(key()).isNull()) { return true; } _reportError(qBindExpectedNull ); return false; } + bool trySequence(quint32* =nullptr) { if (levels.last().isGroup) { levels.push(Level( )); return true; } reportError(qBindExpectedSequence); return false; } + bool tryRecord (quint32* =nullptr) { if (levels.last().isGroup) { levels.push(Level(qBindExpectedItem)); return true; } reportError(qBindExpectedRecord ); return false; } + bool tryNull ( ) { if (settings->value(key()).isNull()) { return true; } reportError(qBindExpectedNull ); return false; } bool tryItem(QIdentifier& k) { levels.last().key=k ; return true; } bool tryItem( ) { levels.last().idx++ ; return true; } bool tryOut ( ) { levels.pop(); settings->endGroup(); return true; } bool _isOk() const noexcept { return settings; } - void _setChoice(bool v) { isChoice=v; } - void _reportError(QIdentifierLiteral error) { if (!isChoice) errors.append(Error{ error, currentPath() }); } + void reportError(QIdentifierLiteral error, QString context = QString()) { if (errorHandler) errorHandler(error, QString(currentPath().latin1()).append(context)); } private: template - bool set(T& t, QIdentifierLiteral error) { QVariant v = settings->value(key()); if (v.convert(qMetaTypeId())) { t = v.value(); return true; } _reportError(error); return false; } + bool set(T& t, QIdentifierLiteral error) { QVariant v = settings->value(key()); if (v.convert(qMetaTypeId())) { t = v.value(); return true; } reportError(error); return false; } QString key() { Q_ASSERT(!levels.isEmpty()); return !levels.last().key.isNull() diff --git a/tests/QBind/QValue.h b/tests/QBind/QValue.h index c6a9dca643b6144b0c3e6165bb1111527a7d9905..0e2b5f1b86d6d5d76986a8bf9e26676eb5f3deff 100644 --- a/tests/QBind/QValue.h +++ b/tests/QBind/QValue.h @@ -49,6 +49,8 @@ Class (Class&& o) noexcept { Statements } \ Class& operator=(Class&& o) noexcept { if (this!=&o) { Statements } return *this; } +#include + #include "QData.h" // ////////////////////////////////////////////////////////////////////////// @@ -111,6 +113,8 @@ extern QIdentifierLiteral qmColor; enum QValueMode { Invalid=0, Read=1, Write=2 }; //!< Specifies QTransmogrifier::zap traversal and processing (the design would support other QValueMode like Append or Diff) +using QValueErrorHandler = std::function; + struct BindGeneric {}; struct BindNative {}; //!< Specifies whether QVal calls QAbstractValue::tryBind or QTransmogrifier::bind @@ -224,8 +228,8 @@ struct QAbstractValue { //! \warning meta() is ignored by default and subject to various interpretation, so users should define or adopt existing meta data standards like XSD for sake of interoperability virtual void _meta(QIdentifierLiteral&, QAsciiData&) {} - virtual void _setChoice(bool) {} - virtual void _reportError(QIdentifierLiteral) {} + virtual QValueErrorHandler setErrorHandler(QValueErrorHandler newHandler = QValueErrorHandler()) { Q_UNUSED(newHandler) return QValueErrorHandler(); } + virtual void reportError(QIdentifierLiteral name, QString context = QString()) { Q_UNUSED(name) Q_UNUSED(context) } }; // ////////////////////////////////////////////////////////////////////////// @@ -262,9 +266,9 @@ public: QValueStatus* operator ->() noexcept { return this; } QVal value() noexcept ; - void setChoice (bool c) { if (impl) impl->_setChoice (c); } - void reportError(QIdentifierLiteral e) { if (impl) impl->_reportError(e); } - void reportError(const char* e) { reportError(QIdentifierLiteral(e)); } + QValueErrorHandler setErrorHandler(QValueErrorHandler newHandler = nullptr) { return Q_LIKELY(impl) ? impl->setErrorHandler(newHandler) : QValueErrorHandler(); } + void reportError(QIdentifierLiteral name, QString context = QString()) { if (Q_LIKELY(impl)) impl->reportError(name, context); } + void reportError(const char* asciiName, QString context = QString()) { reportError(QIdentifierLiteral(asciiName), context); } protected: template friend class QVal; // enables calling methods below template friend class QSeq; @@ -480,16 +484,6 @@ struct QTransmogrifier { template bool QValueStatus::tryBind(BindGeneric, T&& t) { return QTransmogrifier>::zap(QValue(_unsafeCopy()),std::forward(t)); } -class QScopedChoice //!< for accurate error reporting -{ - Q_DISABLE_COPY(QScopedChoice) -public: - QScopedChoice(QValue& t) : t(t) { if(t) t->setChoice(true ); } - ~QScopedChoice( ) { if(t) t->setChoice(false); } -private: - QValue& t; -}; - // ////////////////////////////////////////////////////////////////////////// // Base QAbstractValue implementations for Read and Write QValueMode static char *qulltoa2(char *p, qulonglong n, int base=10) //!< Reproduced here for benchmark purposes only @@ -662,28 +656,28 @@ struct QAbstractValueReader : public QAbstractValue virtual bool tryItem(QIdentifierLiteral n) { QIdentifier id; return tryItem(id) && id.utf8()==n.utf8(); } virtual bool tryBind( const char* u) { return tryBind(QUtf8DataView(u)); } - virtual bool tryBind( QUtf8DataView u) { QUtf8Data r; if (tryBind(r) && r.utf8()==u.data()) return true; _reportError(qBindExpectedConstant); return false; } - virtual bool tryBind(QAsciiDataView a) { QUtf8Data r; if (tryBind(r) && r.utf8()==a.data()) return true; _reportError(qBindExpectedConstant); return false; } - virtual bool tryBind( QLatin1String l) { QString r; if (tryBind(r) && r==l ) return true; _reportError(qBindExpectedConstant); return false; } - virtual bool tryBind( QStringView s) { QString r; if (tryBind(r) && r==s ) return true; _reportError(qBindExpectedConstant); return false; } + virtual bool tryBind( QUtf8DataView u) { QUtf8Data r; if (tryBind(r) && r.utf8()==u.data()) return true; reportError(qBindExpectedConstant); return false; } + virtual bool tryBind(QAsciiDataView a) { QUtf8Data r; if (tryBind(r) && r.utf8()==a.data()) return true; reportError(qBindExpectedConstant); return false; } + virtual bool tryBind( QLatin1String l) { QString r; if (tryBind(r) && r==l ) return true; reportError(qBindExpectedConstant); return false; } + virtual bool tryBind( QStringView s) { QString r; if (tryBind(r) && r==s ) return true; reportError(qBindExpectedConstant); return false; } virtual bool tryBind( QUtf8Data& s) = 0; virtual bool tryBind( QString& s) { QUtf8Data u; if (!tryBind(u)) return false; s=QString::fromUtf8(u.utf8()); return true; } virtual bool tryBind( bool& b) { QUtf8Data u; if (!tryBind(u)) return false; if (u.utf8().compare("true" , Qt::CaseInsensitive)==0) { b=true ; } else - if (u.utf8().compare("false", Qt::CaseInsensitive)==0) { b=false; } else { _reportError(qBindExpectedBoolean ); return false; } return true; } - virtual bool tryBind( qint8& n) { qint64 l; if (!tryBind(l)) return false; if (l::min() || std::numeric_limits< qint8>::max()::max()::min() || std::numeric_limits< qint16>::max()::max()::min() || std::numeric_limits< qint32>::max()::max()::min() || std::numeric_limits< qint8>::max()::max()::min() || std::numeric_limits< qint16>::max()::max()::min() || std::numeric_limits< qint32>::max()::max() struct QTransmogrifier> { static QValueStatus zap(QValue&& v, QDefaultValue&& t) { if (v->mode()==Read) { - v->setChoice(true); + auto suspended = v->setErrorHandler(); auto r=v.null(); - v->setChoice(false); + v->setErrorHandler(suspended); if (r) { t.value = t.defaultValue; return r; @@ -1143,56 +1142,55 @@ struct QTransmogrifier { static QValueStatus zap(QValue&& src, QValue&& dst) { if (src->mode()==Read && dst->mode()==Write) { QValueStatus srcRes; - { - QScopedChoice choice(src); - QSequence srcSeq; QSequence dstSeq; - if ( (srcSeq = src.sequence())) { - /**/ dstSeq = dst.sequence(); - - QValue srcVal; QValue dstVal; - while((srcVal = srcSeq.unsafeItem())) { // using item()'s QVal>,_> functions - dstVal = dstSeq.unsafeItem(); - srcSeq = QSequence(srcVal.bind(std::move(dstVal))); - } - - /**/ dstSeq.out(); - return srcSeq.out(); + auto suspended = src->setErrorHandler(); + QSequence srcSeq; QSequence dstSeq; + if ( (srcSeq = src.sequence())) { + /**/ dstSeq = dst.sequence(); + + QValue srcVal; QValue dstVal; + while((srcVal = srcSeq.unsafeItem())) { // using item()'s QVal>,_> functions + dstVal = dstSeq.unsafeItem(); + srcSeq = QSequence(srcVal.bind(std::move(dstVal))); } - QRecord srcRec; QRecord dstRec; - if ( (srcRec = src.record())) { - /**/ dstRec = dst.record(); - - QIdentifier srcKey; QValue srcVal; QValue dstVal; - while((srcVal = srcRec.unsafeItem(srcKey))) { - dstVal = dstRec.unsafeItem(srcKey); - srcRec = QRecord(srcVal.bind(std::move(dstVal))); - } - /**/ dstRec.out(); - return srcRec.out(); - } - if ( (srcRes = src.null())) { - return dst.null(); + /**/ dstSeq.out(); + return srcSeq.out(); + } + QRecord srcRec; QRecord dstRec; + if ( (srcRec = src.record())) { + /**/ dstRec = dst.record(); + + QIdentifier srcKey; QValue srcVal; QValue dstVal; + while((srcVal = srcRec.unsafeItem(srcKey))) { + dstVal = dstRec.unsafeItem(srcKey); + srcRec = QRecord(srcVal.bind(std::move(dstVal))); } - 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; } - // 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 - // TODO Other BindNative types we do not want to treat as text: QDateTime, QUuid + + /**/ 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; } + // 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 + // TODO Other BindNative types we do not want to treat as text: QDateTime, QUuid + src->setErrorHandler(suspended); srcRes.reportError("ExpectedSequenceRecordNullTextBytesBooleanIntegerDecimal"); return srcRes; } diff --git a/tests/QBind/QVariant_impl.h b/tests/QBind/QVariant_impl.h index 855c0fae3b1bd50886e75bbf59fcb4337c4a319a..4e69ffaec25d3e952cc03b0ecfbfec614bf1c173 100644 --- a/tests/QBind/QVariant_impl.h +++ b/tests/QBind/QVariant_impl.h @@ -120,10 +120,7 @@ class QVariantVisitor : public QAbstractValueReader public: Q_ENABLE_MOVE_DEFAULT(QVariantVisitor) QVariantVisitor(const QVariant* v) : value(v) { Q_ASSERT(v); } - void reset(const QVariant* v) { value=v; Q_ASSERT(v); levels.resize(0); errors.resize(0); } - - struct Error { QIdentifierLiteral error; QAsciiData path; template T bind(QVal&& value) { return value.bind(QUtf8Data(error.utf8()+' '+path.utf8())); } }; - QVector errors; + void reset(const QVariant* v) { value=v; Q_ASSERT(v); levels.resize(0); } QAsciiData currentPath() { QByteArray path; @@ -137,54 +134,52 @@ protected: // TODO Support _meta to be able to cache and restitute all metadata as well as data+datatype template - bool tryBind(T& t) { if (current()->type()==qMetaTypeId()) { t = current()->value(); return true; } _reportError(QIdentifierLiteral("ExpectedDeclaredMetatypeT")); return false; } + bool tryBind(T& t) { if (current()->type()==qMetaTypeId()) { t = current()->value(); return true; } reportError(QIdentifierLiteral("ExpectedDeclaredMetatypeT")); return false; } bool tryBind( QUtf8Data& t) { if (current()->userType() // TODO ->type() if QUtf8Data lands into Qt - ==qMetaTypeId< QUtf8Data>()) { t = current()->value< QUtf8Data>(); return true; } _reportError(qBindExpectedText ); return false; } - bool tryBind( QString& t) { if (current()->type()==qMetaTypeId< QString>()) { t = current()->value< QString>(); return true; } _reportError(qBindExpectedText ); return false; } - bool tryBind( bool& t) { if (current()->type()==qMetaTypeId< bool>()) { t = current()->value< bool>(); return true; } _reportError(qBindExpectedBoolean ); return false; } - bool tryBind(QByteArray& t) { if (current()->type()==qMetaTypeId()) { t = current()->value(); return true; } _reportError(qBindExpectedBytes ); return false; } + ==qMetaTypeId< QUtf8Data>()) { t = current()->value< QUtf8Data>(); return true; } reportError(qBindExpectedText ); return false; } + bool tryBind( QString& t) { if (current()->type()==qMetaTypeId< QString>()) { t = current()->value< QString>(); return true; } reportError(qBindExpectedText ); return false; } + bool tryBind( bool& t) { if (current()->type()==qMetaTypeId< bool>()) { t = current()->value< bool>(); return true; } reportError(qBindExpectedBoolean ); return false; } + bool tryBind(QByteArray& t) { if (current()->type()==qMetaTypeId()) { t = current()->value(); return true; } reportError(qBindExpectedBytes ); return false; } // Convert numerical types to strictly larger ones // TODO convert all compatible values - bool tryBind( qint8& t) { if (current()->type()==qMetaTypeId< qint8>()) { t = current()->value< qint8>(); return true; } _reportError(qBindExpectedInteger ); return false; } + bool tryBind( qint8& t) { if (current()->type()==qMetaTypeId< qint8>()) { t = current()->value< qint8>(); return true; } reportError(qBindExpectedInteger ); return false; } bool tryBind( qint16& t) { if (current()->type()==qMetaTypeId< qint8>()|| - current()->type()==qMetaTypeId< qint16>()) { t = current()->value< qint16>(); return true; } _reportError(qBindExpectedInteger ); return false; } + current()->type()==qMetaTypeId< qint16>()) { t = current()->value< qint16>(); return true; } reportError(qBindExpectedInteger ); return false; } bool tryBind( qint32& t) { if (current()->type()==qMetaTypeId< qint8>()|| current()->type()==qMetaTypeId< qint16>()|| - current()->type()==qMetaTypeId< qint32>()) { t = current()->value< qint32>(); return true; } _reportError(qBindExpectedInteger ); return false; } + current()->type()==qMetaTypeId< qint32>()) { t = current()->value< qint32>(); return true; } reportError(qBindExpectedInteger ); return false; } bool tryBind( qint64& t) { if (current()->type()==qMetaTypeId< qint8>()|| current()->type()==qMetaTypeId< qint16>()|| current()->type()==qMetaTypeId< qint32>()|| - current()->type()==qMetaTypeId< qint64>()) { t = current()->value< qint64>(); return true; } _reportError(qBindExpectedInteger ); return false; } - bool tryBind( quint8& t) { if (current()->type()==qMetaTypeId< quint8>()) { t = current()->value< quint8>(); return true; } _reportError(qBindExpectedPositiveInteger); return false; } + current()->type()==qMetaTypeId< qint64>()) { t = current()->value< qint64>(); return true; } reportError(qBindExpectedInteger ); return false; } + bool tryBind( quint8& t) { if (current()->type()==qMetaTypeId< quint8>()) { t = current()->value< quint8>(); return true; } reportError(qBindExpectedPositiveInteger); return false; } bool tryBind( quint16& t) { if (current()->type()==qMetaTypeId< quint8>()|| - current()->type()==qMetaTypeId< quint16>()) { t = current()->value< quint16>(); return true; } _reportError(qBindExpectedPositiveInteger); return false; } + current()->type()==qMetaTypeId< quint16>()) { t = current()->value< quint16>(); return true; } reportError(qBindExpectedPositiveInteger); return false; } bool tryBind( quint32& t) { if (current()->type()==qMetaTypeId< quint8>()|| current()->type()==qMetaTypeId< quint16>()|| - current()->type()==qMetaTypeId< quint32>()) { t = current()->value< quint32>(); return true; } _reportError(qBindExpectedPositiveInteger); return false; } + current()->type()==qMetaTypeId< quint32>()) { t = current()->value< quint32>(); return true; } reportError(qBindExpectedPositiveInteger); return false; } bool tryBind( quint64& t) { if (current()->type()==qMetaTypeId< quint8>()|| current()->type()==qMetaTypeId< quint16>()|| current()->type()==qMetaTypeId< quint32>()|| - current()->type()==qMetaTypeId< quint64>()) { t = current()->value< quint64>(); return true; } _reportError(qBindExpectedPositiveInteger); return false; } - bool tryBind( float& t) { if (current()->type()==qMetaTypeId< float>()) { t = current()->value< float>(); return true; } _reportError(qBindExpectedDecimal ); return false; } + current()->type()==qMetaTypeId< quint64>()) { t = current()->value< quint64>(); return true; } reportError(qBindExpectedPositiveInteger); return false; } + bool tryBind( float& t) { if (current()->type()==qMetaTypeId< float>()) { t = current()->value< float>(); return true; } reportError(qBindExpectedDecimal ); return false; } bool tryBind( double& t) { if (current()->type()==qMetaTypeId< float>()|| - current()->type()==qMetaTypeId< double>()) { t = current()->value< double>(); return true; } _reportError(qBindExpectedDecimal ); return false; } + current()->type()==qMetaTypeId< double>()) { t = current()->value< double>(); return true; } reportError(qBindExpectedDecimal ); return false; } - bool trySequence(quint32* =nullptr) { if (current()->type()==QVariant::List ) { levels.push(Level()); return true; } _reportError(qBindExpectedSequence); return false; } - bool tryRecord (quint32* =nullptr) { if (current()->type()==QVariant::Map ) { levels.push(Level()); return true; } _reportError(qBindExpectedRecord ); return false; } - bool tryNull ( ) { if (current()->isNull() ) { return true; } _reportError(qBindExpectedNull ); return false; } + bool trySequence(quint32* =nullptr) { if (current()->type()==QVariant::List ) { levels.push(Level()); return true; } reportError(qBindExpectedSequence); return false; } + bool tryRecord (quint32* =nullptr) { if (current()->type()==QVariant::Map ) { levels.push(Level()); return true; } reportError(qBindExpectedRecord ); return false; } + bool tryNull ( ) { if (current()->isNull() ) { return true; } reportError(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(); } bool tryOut ( ) { levels.pop() ; return true; } bool _isOk() const noexcept { return value; } - void _setChoice(bool v) { isChoice=v; } - void _reportError(QIdentifierLiteral error) { if (!isChoice) errors.append(Error{ error, currentPath() }); } + void reportError(QIdentifierLiteral error, QString context = QString()) { if (errorHandler) errorHandler(error, QString(currentPath().latin1()).append(context)); } private: const QVariant* current(unsigned outer=0) const { return unsigned(levels.size())-outer <= 0 ? value : &(levels[unsigned(levels.size())-outer-1].item); } const QVariant* value; struct Level { QIdentifier key=QIdentifier(); int idx=-1; QVariant item; Level() = default; }; QStack levels; - bool isChoice = false; }; diff --git a/tests/QBind/main.cpp b/tests/QBind/main.cpp index 2946c883e26a50ba70f99990824e454a49f00155..6206b91cf7857cab3f23b3b95ac9ec8a9e694379 100644 --- a/tests/QBind/main.cpp +++ b/tests/QBind/main.cpp @@ -412,7 +412,7 @@ int main(int argc, char *argv[]) << color ; } - STOP("QDebug",s); + STOP("QDebug",s) START { ba.resize(0); TextWriter(&ba) @@ -423,7 +423,7 @@ int main(int argc, char *argv[]) << color ; } - STOP("Text",b.buffer()); + STOP("Text",b.buffer()) START { ba.resize(0); QJsonWriter(&ba).sequence() @@ -434,7 +434,7 @@ int main(int argc, char *argv[]) .bind(color) ; } - STOP("Json",ba); + STOP("Json",ba) START { ba.resize(0); QXmlStreamWriter w(&ba); @@ -446,7 +446,7 @@ int main(int argc, char *argv[]) .bind(color) ; } - STOP("Xml",ba); + STOP("Xml",ba) START { v.clear(); QVariantBuilder(&v).sequence() @@ -457,7 +457,7 @@ int main(int argc, char *argv[]) .bind(color) ; } - STOP("Variant",Text(v).utf8()); + STOP("Variant",Text(v).utf8()) START { ba.resize(0); QCborWriter(&ba).value().sequence(5) @@ -468,7 +468,7 @@ int main(int argc, char *argv[]) .bind(color) ; } - STOP("Cbor",ba.toHex()); + STOP("Cbor",ba.toHex()) START { b.seek(0); b.buffer().resize(0); QCborStreamWriter s(&b); @@ -487,7 +487,7 @@ int main(int argc, char *argv[]) s.endMap(); s.endArray(); } - STOP("QCborStream",b.buffer().toHex()); + STOP("QCborStream",b.buffer().toHex()) START { b.seek(0); b.buffer().resize(0); QDataStream d(&b); @@ -499,7 +499,7 @@ int main(int argc, char *argv[]) .bind(color) ; } - STOP("Data",b.buffer().toHex()); + STOP("Data",b.buffer().toHex()) START { b.seek(0); b.buffer().resize(0); QDataStream(&b) @@ -509,7 +509,7 @@ int main(int argc, char *argv[]) << false << color; } - STOP("QDataStream",b.buffer().toHex()); + STOP("QDataStream",b.buffer().toHex()) float f = 1.333333333333f; bool boolean = false; START { ba.resize(0); @@ -519,7 +519,7 @@ int main(int argc, char *argv[]) ba.append(reinterpret_cast(&boolean),sizeof(boolean)); ba.append(reinterpret_cast(&color ),sizeof(color )); // schema evolution must be specified externally } - STOP("QByteArray",ba.toHex()); + STOP("QByteArray",ba.toHex()) QBindable bindables[5]; START { @@ -529,19 +529,19 @@ int main(int argc, char *argv[]) bindables[3] = false; bindables[4] = color; } - STOP("Bindables",""); + STOP("Bindables","") START { ba.resize(0); QCborWriter(&ba).bind(bindables); } - STOP("Bindables>Cbor",ba.toHex()); + STOP("Bindables>Cbor",ba.toHex()) START { ba.resize(0); QJsonWriter(&ba).bind(bindables); } - STOP("Bindables>Json",ba); + STOP("Bindables>Json",ba) } - GROUP_STOP; + GROUP_STOP GROUP("doubles>")//======================================================== { START { @@ -549,14 +549,14 @@ int main(int argc, char *argv[]) QDebug d(&s); for (int i=0; i < 16; i++) { d << transform[i]; - }; + } } - STOP("QDebug",s); + STOP("QDebug",s) START { ba.resize(0); TextWriter(&ba).bind(transform); } - STOP("Text",b.buffer()); + STOP("Text",b.buffer()) START { ba.resize(0); QJsonWriter(&ba).bind(transform); @@ -568,17 +568,17 @@ int main(int argc, char *argv[]) QXmlWriter d(&w); d.bind(transform); } - STOP("Xml",ba); + STOP("Xml",ba) START { v.clear(); QVariantBuilder(&v).bind(transform); } - STOP("Variant",Text(v).utf8()); + STOP("Variant",Text(v).utf8()) START { b.seek(0); b.buffer().resize(0); QCborWriter(&ba).bind(transform); } - STOP("Cbor",ba.toHex()); + STOP("Cbor",ba.toHex()) START { b.seek(0); b.buffer().resize(0); @@ -587,25 +587,25 @@ int main(int argc, char *argv[]) s.startArray(size); for (quint8 i=0; i < size; i++) { s.append(transform[i]); - }; + } s.endArray(); } - STOP("QCborStream",b.buffer().toHex()); + STOP("QCborStream",b.buffer().toHex()) START { b.seek(0); b.buffer().resize(0); QDataStream d(&b); QDataWriter(&d).value().bind(transform); } - STOP("Data",b.buffer().toHex()); + STOP("Data",b.buffer().toHex()) START { b.seek(0); b.buffer().resize(0); QDataStream data(&b); data << transform.size(); for (int i=0; i < transform.size(); i++) { data << transform[i]; - }; + } } - STOP("QDataStream",b.buffer().toHex()); + STOP("QDataStream",b.buffer().toHex()) QByteArray str; str.reserve(500); START { str.resize(0); @@ -613,25 +613,25 @@ int main(int argc, char *argv[]) str.append(reinterpret_cast(&size),sizeof(size)); for (int i=0; i < size; i++) { str.append(reinterpret_cast(&transform[i]),sizeof(transform[i])); - }; + } } - STOP("QByteArray",str.toHex()); + STOP("QByteArray",str.toHex()) QBindable bindable; START { bindable = transform; } - STOP("Bindable",""); + STOP("Bindable","") START { ba.resize(0); QCborWriter(&ba).bind(bindable); } - STOP("Bindable>Cbor",ba.toHex()); + STOP("Bindable>Cbor",ba.toHex()) START { ba.resize(0); QJsonWriter(&ba).bind(bindable); } - STOP("Bindable>Json",ba); + STOP("Bindable>Json",ba) } GROUP_STOP GROUP("Person>")//========================================================= @@ -641,12 +641,12 @@ int main(int argc, char *argv[]) QDebug dbg(&s); dbg << person; } - STOP("QDebug",s); + STOP("QDebug",s) START { ba.resize(0); TextWriter(&ba).bind(person); } - STOP("Text",b.buffer()); + STOP("Text",b.buffer()) START { ba.resize(0); QJsonWriter(&ba).bind(person); @@ -659,17 +659,17 @@ int main(int argc, char *argv[]) d.bind(person); w.writeEndDocument(); // FIXME It should not be necessary } - STOP("Xml",ba); + STOP("Xml",ba) START { v.clear(); QVariantBuilder(&v).bind(person); } - STOP("Variant",Text(v).utf8()); + STOP("Variant",Text(v).utf8()) START { ba.resize(0); QCborWriter(&ba).bind(person); } - STOP("Cbor",ba.toHex()); + STOP("Cbor",ba.toHex()) START { b.seek(0); b.buffer().resize(0); QCborStreamWriter s(&b); @@ -695,19 +695,19 @@ int main(int argc, char *argv[]) s.endArray(); s.endMap(); } - STOP("QCborStream",b.buffer().toHex()); + STOP("QCborStream",b.buffer().toHex()) START { b.seek(0); b.buffer().resize(0); QDataStream d(&b); QDataWriter(&d).value().bind(person); } - STOP("Data",b.buffer().toHex()); + STOP("Data",b.buffer().toHex()) START { b.seek(0); b.buffer().resize(0); QDataStream d(&b); d << person; } - STOP("QDataStream",b.buffer().toHex()); + STOP("QDataStream",b.buffer().toHex()) QByteArray str; str.reserve(100); int s; START { str.resize(0); @@ -727,7 +727,7 @@ int main(int argc, char *argv[]) Q_ASSERT(false); // Would need recursion } } - STOP("QByteArray",str.toHex()); + STOP("QByteArray",str.toHex()) #ifdef PROTOBUF std::string buf; pb::Person pb; @@ -765,17 +765,17 @@ int main(int argc, char *argv[]) START { bindable = person; } - STOP("Bindable",""); + STOP("Bindable","") START { ba.resize(0); QCborWriter(&ba).bind(bindable); } - STOP("Bindable>Cbor",ba.toHex()); + STOP("Bindable>Cbor",ba.toHex()) START { ba.resize(0); QJsonWriter(&ba).bind(bindable); } - STOP("Bindable>Json",ba); + STOP("Bindable>Json",ba) } GROUP_STOP GROUP("Phone>")//========================================================= @@ -785,12 +785,12 @@ int main(int argc, char *argv[]) QDebug dbg(&s); dbg << phone; } - STOP("QDebug",s); + STOP("QDebug",s) START { ba.resize(0); TextWriter(&ba).bind(phone); } - STOP("Text",b.buffer()); + STOP("Text",b.buffer()) START { ba.resize(0); QJsonWriter(&ba).bind(phone); @@ -802,17 +802,17 @@ int main(int argc, char *argv[]) QXmlWriter d(&w); d.bind(phone); } - STOP("Xml",ba); + STOP("Xml",ba) START { v.clear(); QVariantBuilder(&v).bind(phone); } - STOP("Variant",Text(v).utf8()); + STOP("Variant",Text(v).utf8()) START { ba.resize(0); QCborWriter(&ba).bind(phone); } - STOP("Cbor",ba.toHex()); + STOP("Cbor",ba.toHex()) START { b.seek(0); b.buffer().resize(0); QCborStreamWriter s(&b); @@ -821,44 +821,48 @@ int main(int argc, char *argv[]) s.append("number");s.append(phone._n); s.endMap(); } - STOP("QCborStream",b.buffer().toHex()); + STOP("QCborStream",b.buffer().toHex()) START { b.seek(0); b.buffer().resize(0); QDataStream d(&b); QDataWriter(&d).value().bind(phone); } - STOP("Data",b.buffer().toHex()); + STOP("Data",b.buffer().toHex()) START { b.seek(0); b.buffer().resize(0); QDataStream d(&b); d << phone; } - STOP("QDataStream",b.buffer().toHex()); + STOP("QDataStream",b.buffer().toHex()) QByteArray str; str.reserve(100); START { str.resize(0); str.append(reinterpret_cast(&phone._t),sizeof(phone._t));str.append(phone._n); } - STOP("QByteArray",str.toHex()); + STOP("QByteArray",str.toHex()) QBindable bindable; START { bindable = phone; } - STOP("Bindable",""); + STOP("Bindable","") START { ba.resize(0); QCborWriter(&ba).bind(bindable); } - STOP("Bindable>Cbor",ba.toHex()); + STOP("Bindable>Cbor",ba.toHex()) START { ba.resize(0); QJsonWriter(&ba).bind(bindable); } - STOP("Bindable>Json",ba); + STOP("Bindable>Json",ba) } GROUP_STOP + QStringList errors; + auto handler = [&errors](QIdentifierLiteral error, QString context) { + errors.append(context.append(error.latin1())); + }; GROUP("Person<>Json")//==================================================== { QBuffer json; json.open(QIODevice::ReadOnly); @@ -867,49 +871,47 @@ int main(int argc, char *argv[]) Person p; QBuffer roundtrip; roundtrip.open(QIODevice::ReadWrite); QJsonValue jv; - QVector readerErrors; - QVector visitorErrors; //--------------------------------------------------------------------- START { - json.seek(0); p = {}; + json.seek(0); p = {}; errors.clear(); QJsonReader r(&json); + r.setErrorHandler(handler); r.bind(p); - readerErrors = r.errors; } - STOP("Json>P",Text(p).utf8()+" | "+Text(readerErrors)) + STOP("Json>P",Text(p).utf8()+" | "+errors.join(", ")) START { roundtrip.seek(0); QJsonWriter(&roundtrip).bind(p); } STOP("P>Json",QString(roundtrip.buffer())) START { - roundtrip.seek(0); p = {}; + roundtrip.seek(0); p = {}; errors.clear(); QJsonReader r(&roundtrip); + r.setErrorHandler(handler); r.bind(p); - readerErrors = r.errors; } - STOP("Json>P",Text(p).utf8()+" | "+Text(readerErrors)) + STOP("Json>P",Text(p).utf8()+" | "+errors.join(", ")) //--------------------------------------------------------------------- START { jv = QJsonValue(); QJsonBuilder(&jv).bind(p); } - STOP("P>JsonValue",QJsonDocument(jv.toObject()).toJson(QJsonDocument::Compact)); + STOP("P>JsonValue",QJsonDocument(jv.toObject()).toJson(QJsonDocument::Compact)) START { - p = {}; + p = {}; errors.clear(); QJsonVisitor r(&jv); + r.setErrorHandler(handler); r.bind(p); - visitorErrors = r.errors; } - STOP("JsonValue>P",Text(p).utf8()+" | "+Text(visitorErrors)) + STOP("JsonValue>P",Text(p).utf8()+" | "+errors.join(", ")) //--------------------------------------------------------------------- START { - roundtrip.seek(0); jv = QJsonValue(); + roundtrip.seek(0); jv = QJsonValue(); errors.clear(); QJsonReader r(&roundtrip); + r.setErrorHandler(handler); r.bind(jv); - readerErrors = r.errors; } - STOP("Json>JsonValue",QJsonDocument(jv.toObject()).toJson(QJsonDocument::Compact)+" | "+Text(readerErrors)); + STOP("Json>JsonValue",QJsonDocument(jv.toObject()).toJson(QJsonDocument::Compact)+" | "+errors.join(", ")); // TODO // START { // ba.resize(0); @@ -919,8 +921,8 @@ int main(int argc, char *argv[]) START { roundtrip.seek(0); ba.resize(0); QJsonReader r(&roundtrip); + r.setErrorHandler(handler); r.bind(QCborWriter(&ba).value()); - readerErrors = r.errors; } STOP("Json>Cbor",ba.toHex()) } @@ -934,28 +936,26 @@ int main(int argc, char *argv[]) Person p; QBuffer roundtrip; roundtrip.open(QIODevice::ReadWrite); QCborValue cv; - QVector readerErrors; - QVector visitorErrors; //--------------------------------------------------------------------- START { - cbor.seek(0); p = {}; + cbor.seek(0); p = {}; errors.clear(); QCborReader r(&cbor); + r.setErrorHandler(handler); r.bind(p); - readerErrors = r.errors; } - STOP("Cbor>P",Text(p).utf8()+" | "+Text(readerErrors)) + STOP("Cbor>P",Text(p).utf8()+" | "+errors.join(", ")) START { roundtrip.buffer().resize(0); QCborWriter(&roundtrip.buffer()).bind(p); } STOP("P>Cbor",QString(roundtrip.buffer().toHex())) START { - roundtrip.seek(0); p = {}; + roundtrip.seek(0); p = {}; errors.clear(); QCborReader r(&roundtrip); + r.setErrorHandler(handler); r.bind(p); - readerErrors = r.errors; } - STOP("Cbor>P",Text(p).utf8()+" | "+Text(readerErrors)) + STOP("Cbor>P",Text(p).utf8()+" | "+errors.join(", ")) //--------------------------------------------------------------------- QCborStreamReader reader(roundtrip.buffer()); bool parsed; @@ -1053,20 +1053,20 @@ int main(int argc, char *argv[]) } STOP("P>CborValue",QString(cv.toCbor().toHex())) START { - p = {}; + p = {}; errors.clear(); QCborVisitor r(&cv); + r.setErrorHandler(handler); r.bind(p); - visitorErrors = r.errors; } - STOP("CborValue>P",Text(p).utf8()+" | "+Text(visitorErrors)) + STOP("CborValue>P",Text(p).utf8()+" | "+errors.join(", ")) //--------------------------------------------------------------------- START { - roundtrip.seek(0); cv = QCborValue(); + roundtrip.seek(0); cv = QCborValue(); errors.clear(); QCborReader r(&roundtrip); + r.setErrorHandler(handler); r.bind(cv); - readerErrors = r.errors; } - STOP("Cbor>CborValue",cv.toDiagnosticNotation()+" | "+Text(readerErrors)); + STOP("Cbor>CborValue",cv.toDiagnosticNotation()+" | "+errors.join(", ")); START { ba.resize(0); QCborWriter(&ba).bind(cv); @@ -1074,12 +1074,12 @@ int main(int argc, char *argv[]) STOP("CborValue>Cbor",ba.toHex()); //--------------------------------------------------------------------- START { - roundtrip.seek(0); b.seek(0); b.buffer().resize(0); + roundtrip.seek(0); b.seek(0); b.buffer().resize(0); errors.clear(); QCborReader r(&roundtrip); + r.setErrorHandler(handler); r.bind(QJsonWriter(&b).value()); - readerErrors = r.errors; } - STOP("Cbor>Json",b.buffer()+" | "+Text(readerErrors)) + STOP("Cbor>Json",b.buffer()+" | "+errors.join(", ")) } GROUP_STOP GROUP("Person<>Settings")//==================================================== @@ -1088,18 +1088,17 @@ int main(int argc, char *argv[]) QSettings::setPath(QSettings::IniFormat,QSettings::UserScope,iniFile.path()); QSettings ini(QSettings::IniFormat,QSettings::UserScope,"QTransmogrifier","sample"); QSettings nat( QSettings::UserScope,"QTransmogrifier","sample"); - QVector errors; //--------------------------------------------------------------------- START { QSettingsWriter(&nat).bind(person); } STOP("P>Settings",Text(person).utf8()) START { - QSettingsReader r(&nat); + QSettingsReader r(&nat); errors.clear(); + r.setErrorHandler(handler); r.bind(person); - errors = r.errors; } - STOP("Settings>P",Text(person).utf8()+" | "+Text(errors)) + STOP("Settings>P",Text(person).utf8()+" | "+errors.join(", ")) START { QSettingsWriter(&ini).bind(person); } @@ -1144,6 +1143,9 @@ void doGuiExample() { persons[0].height=1.72; persons[1].phones.append(Phone{Phone::Office,"112"}); + QStringList errors; + auto handler = [&errors](QIdentifierLiteral name, QString context){ errors.append(context.append(name.latin1())); }; + // Various possibilities to customize bind #if 0 @@ -1179,8 +1181,7 @@ void doGuiExample() { if (0<=currentTransformTab) { QModelReader<> reader(transformTabs[currentTransformTab].model, false); transformTabs[currentTransformTab].bind(reader.value()); - TextWriter(&text).bind(reader.errors); - text.append('\n'); + text = errors.join('\n').toUtf8().append('\n'); } QJsonWriter(&text).bind(transform); r0Label->setText(QString::fromUtf8(text)); @@ -1228,7 +1229,7 @@ void doGuiExample() { return v.null(); }) .out(); - } + } return std::move(s); // So caller stops calling QAbstractValue if user function was unable to keep track of the active QValueStatus });}}, @@ -1243,8 +1244,9 @@ void doGuiExample() { QByteArray text; if (0<=currentPersonsTab) { QModelReader<> reader(personsTabs[currentPersonsTab].model); + reader.setErrorHandler(handler); personsTabs[currentPersonsTab].bind(reader.value()); - TextWriter(&text).bind(reader.errors); + TextWriter(&text).bind(errors.join("\n")); text.append('\n'); } QJsonWriter(&text).bind(persons);