Vous avez reçu un message "Your GitLab account has been locked ..." ? Pas d'inquiétude : lisez cet article https://docs.gricad-pages.univ-grenoble-alpes.fr/help/unlock/

Commit fa1426e4 authored by EXT Arnaud Clère's avatar EXT Arnaud Clère
Browse files
parents e5968210 90cb6e83
......@@ -64,25 +64,25 @@ end
The automaton is implemented as follows:
- `QVal<_>`, `QRec<_>` and `QSeq<_>` types implement possible states where _ denotes the type of outer state
- State types only expose public methods corresponding to possible transitions, that return the destination state type
- The initial state type is `QValue` and the final state is `QValueStatus` (for instance: `QValue::null()` returns a `QValueStatus`)
- Returning a `QRec<_>` or `QSeq<_>` type automatically convert to the final `QValueStatus` type invoking as much `out()` transitions as required
- `QValueStatus` is a non-owning pointer to an `QAbstractValue` interface which implementations translate data traversal into specific
- The initial state type is `QValue` and the final state is `QCur` (for instance: `QValue::null()` returns a `QCur`)
- Returning a `QRec<_>` or `QSeq<_>` type automatically convert to the final `QCur` type invoking as much `out()` transitions as required
- `QCur` is a non-owning pointer to an `QAbstractValue` interface which implementations translate data traversal into specific
data format read/write actions
- `QValueStatus` instance is moved from the start state type to the end state type only for successful transitions, allowing to test
- `QCur` instance is moved from the start state type to the end state type only for successful transitions, allowing to test
alternatives before proceeding with the traversal
- Transitions may fail for various reasons specific to `QAbstractValue` implementations:
- builders may not be able to allocate new items
- readers may read data not matching the expected transition
- ...
- In case of unsuccessfull transition the returned state type receives a null `QValueStatus` that transparently bypasses calls to `QAbstractValue`
- In case of unsuccessfull transition the returned state type receives a null `QCur` that transparently bypasses calls to `QAbstractValue`
- `bind<T>()` calls are forwarded to the actual `QAbstractValue` or generic `QTransmogrifier` depending on `BindSupport<T>`:
- BindNative : **QAbstractValue** interface method
- BindGeneric : **QTransmogrifier** template specialization for T
- Every `bind<T>()` starts from a QValue which is an un *unsafe* QValueStatus copy wrt well-formedness (these `unsafeItem()` copies are protected from incorrect use)
- Every `bind<T>()` starts from a QValue which is an un *unsafe* QCur copy wrt well-formedness (these `unsafeItem()` copies are protected from incorrect use)
## C++ types extensibility
QTransmogrifier is a functor templated on T type receiving a Value and T reference (either lvalue or rvalue reference) and returning the QValueStatus.
QTransmogrifier is a functor templated on T type receiving a Value and T reference (either lvalue or rvalue reference) and returning the QCur.
Template specializations can be defined for any T and optionally refined for specific Cur<TImpl> with different sets of BindNative types.
A default QTransmogrifier specialization attempts to call `T::bind(...)` to conveniently bind `T* this` without having to understand template syntax,
......@@ -96,28 +96,28 @@ editor will propose to either `bind(myData.item)`, or to construct a `sequence()
## Well-formedness guarantees
Thanks to this design, the compiler will make sure that the only possibility to return a QValueStatus from a `QValue` is to traverse
Thanks to this design, the compiler will make sure that the only possibility to return a QCur from a `QValue` is to traverse
the data without backtracking, calling only and all necessary QAbstractValue virtual methods.
The addition of default and optional values take into account most data schema evolutions in a purely declarative fluent interface without
having to test schema versions and the like. The benefit is that it is not possible to introduce bugs using just the fluent interface.
The downside is that writing loops with the fluent interface is unnatural as one must never forget to follow the valid QValueStatus.
The downside is that writing loops with the fluent interface is unnatural as one must never forget to follow the valid QCur.
For instance:
```cpp
auto seq(v.sequence());
for (auto&& t : ts) {
seq = seq.bind(t); // do not forget to reassign seq, or subsequent items will be `bind` to the moved-from QValueStatus and automatically ignored
seq = seq.bind(t); // do not forget to reassign seq, or subsequent items will be `bind` to the moved-from QCur and automatically ignored
}
```
## Write performance
Since `QValueStatus`, `QVal`, `QRec` and `QSeq` have no data member other than outer types and `QAbstractValue*`, calling their methods can be
Since `QCur`, `QVal`, `QRec` and `QSeq` have no data member other than outer types and `QAbstractValue*`, calling their methods can be
optimized and replaced with just the following operations:
1. test the QAbstractValue pointer validity [^1]
2. call the QAbstractValue virtual method corresponding to the possible transitions
3. return the resulting QValueStatus, QVal, QRec or QSeq with a valid or invalid QValueStatus depending on QAbstractValue method success or failure
3. return the resulting QCur, QVal, QRec or QSeq with a valid or invalid QCur depending on QAbstractValue method success or failure
[^1]: Experiments to use constexpr to bypass this step for writers that always return true did not seem to improve performance.
......
......@@ -108,9 +108,9 @@ public:
QCborWriter(QByteArray* io) : io(io) { Q_ASSERT(io); }
// Shortcuts
/**/ QValue value ( ) { return QValueStatus(this).value(); }
/**/ QSequence sequence(quint32* s=nullptr) { return QValueStatus(this).value().sequence(s); }
template<typename T> QValueStatus bind ( T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
/**/ QValue value ( ) { return QCur(this).value(); }
/**/ QSequence sequence(quint32* s=nullptr) { return QCur(this).value().sequence(s); }
template<typename T> QCur bind ( T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
protected:
bool isValid() const noexcept { return io; }
......@@ -169,7 +169,7 @@ private:
}
QByteArray* io;
std::vector<bool> levels; //!< minimal dynamic context to ensure well-formedness in case QValueStatus is abandoned: true indicates Definite levels with prefixed size
std::vector<bool> levels; //!< minimal dynamic context to ensure well-formedness in case QCur is abandoned: true indicates Definite levels with prefixed size
};
// --------------------------------------------------------------------------
......@@ -186,9 +186,9 @@ public:
QCborBuilder(QCborValue* v) : cbor(v) { Q_ASSERT(v); }
// Shortcuts
/**/ QValue value ( ) { return QValueStatus(this).value(); }
/**/ QSequence sequence(quint32* s=nullptr) { return QValueStatus(this).value().sequence(s); }
template<typename T> QValueStatus bind ( T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
/**/ QValue value ( ) { return QCur(this).value(); }
/**/ QSequence sequence(quint32* s=nullptr) { return QCur(this).value().sequence(s); }
template<typename T> QCur bind ( T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
protected:
bool trySequence(quint32* =nullptr) { levels.push(Step( )); return true; }
bool tryRecord (quint32* =nullptr) { levels.push(Step(qBindExpectedItem )); return true; }
......@@ -245,15 +245,14 @@ public:
}
// Shortcuts
/**/ QValue value ( ) { return QValueStatus(this).value(); }
/**/ QSequence sequence(quint32* s=nullptr) { return QValueStatus(this).value().sequence(s); }
template<typename T> QValueStatus bind ( T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
/**/ QValue value ( ) { return QCur(this).value(); }
/**/ QSequence sequence(quint32* s=nullptr) { return QCur(this).value().sequence(s); }
template<typename T> QCur bind ( T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
protected:
bool trySequence(quint32* =nullptr) { if (current().isArray ()) { steps.push(Step()); return true; } handleError(qBindExpectedSequence); return false; }
bool tryRecord (quint32* =nullptr) { if (current().isMap ()) { steps.push(Step()); return true; } handleError(qBindExpectedRecord ); return false; }
bool tryAny ( ) { if (current().isUndefined()) { return true; } handleError(qBindUnexpectedValue ); return false; }
bool tryNull ( ) { if (current().isNull ()) { return true; } handleError(qBindExpectedNull ); return false; }
bool tryBind ( QUtf8Data& u) { QString s; if (tryBind(s) ) { u = s .toUtf8 (); return true; } return false; }
bool trySequence(quint32* =nullptr) { if (current().isArray ()) { steps.push(Step()); return true; } handleError(qBindExpectedSequence); return false; }
bool tryRecord (quint32* =nullptr) { if (current().isMap ()) { steps.push(Step()); return true; } handleError(qBindExpectedRecord ); return false; }
bool tryNull ( ) { if (current().isNull ()) { 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; }
bool tryBind ( qint64& t) { if (current().isInteger ()) { t = current().toInteger (); return true; } handleError(qBindExpectedInteger ); return false; }
......@@ -265,10 +264,11 @@ protected:
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(); }
bool tryItem( ) { steps.last().idx++; return !(steps.last().item = current(1).toArray(). at(steps.last().idx )).isUndefined(); }
bool tryOut ( ) { steps.pop() ; return true; }
bool isValid() const noexcept { return cbor; }
bool handleError(QIdentifierLiteral e, QString context = QString()) { return errorHandler ? errorHandler(e, QString(currentPath().latin1()).append(context)) : false; }
bool tryOut ( ) { steps.pop() ; return true; }
bool tryAny() { return true; }
bool isValid() const noexcept { return cbor; }
bool handleError(QIdentifierLiteral e, QString context = QString()) { return errorHandler ? errorHandler(e, QString(currentPath().latin1()).append(context)) : false; }
private:
const QCborValue& current(int outer=0) const { Q_ASSERT(0<=outer); return steps.size()-outer <= 0 ? *cbor : steps[steps.size()-outer-1].item; }
......@@ -289,9 +289,9 @@ public:
QCborReader(QIODevice* io) : QCborStreamReader(io), cacheVisitor(&cachedValue) { Q_ASSERT(io); }
// Shortcuts
/**/ QValue value ( ) { return QValueStatus(this).value(); }
/**/ QSequence sequence(quint32* s=nullptr) { return QValueStatus(this).value().sequence(s); }
template<typename T> QValueStatus bind ( T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
/**/ QValue value ( ) { return QCur(this).value(); }
/**/ QSequence sequence(quint32* s=nullptr) { return QCur(this).value().sequence(s); }
template<typename T> QCur bind ( T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
protected:
bool trySequence(quint32* s=nullptr) { if (caching) { cacheLevel++; return caching->trySequence(s); }
skipTag(); if (isArray () && enterContainer()) { levels.push(Level()); return true; } handleError(qBindExpectedSequence); return false; }
......@@ -374,11 +374,11 @@ protected:
else {
cacheReader->setDevice(device());
}
caching = cacheReader.get(); // let outer QValueStatus drive QCborReader depending on bound T
caching = cacheReader.get(); // let outer QCur drive QCborReader depending on bound T
}
else {
cacheVisitor.reset(&cachedValue);
caching = &cacheVisitor; // let outer QValueStatus drive QCborReader depending on bound T
caching = &cacheVisitor; // let outer QCur drive QCborReader depending on bound T
}
return true;
}
......@@ -414,13 +414,11 @@ protected:
return false;
}
return true; }
bool tryOut ( ) { if (caching) { bool out = caching->tryOut(); if (out) { --cacheLevel; cacheOut(); } return out; }
levels.pop();
while (hasNext()) {
tryAny();
}
return leaveContainer(); }
bool tryOut ( ) { if (caching) { bool out = caching->tryOut(); if (out) { --cacheLevel; cacheOut(); } return out; }
levels.pop();
while (hasNext()) { tryAny(); }
return leaveContainer(); }
bool isValid() const noexcept { return const_cast<QCborReader*>(this)->lastError()==QCborError::NoError; }
bool handleError(QIdentifierLiteral e, QString context = QString()) { return errorHandler ? errorHandler(e, QString("at index:%1 ").arg(currentOffset()).append(context)) : false; }
private:
......@@ -465,7 +463,7 @@ private:
template<>
struct QTransmogrifier<QCborValue> {
static QValueStatus zap(QValue&& v, QCborValue&& j) {
static QValueEnd zap(QValue&& v, QCborValue&& j) {
if (v->mode()==Write) {
if (j.isMap ()) return v.bind(j.toMap ());
if (j.isArray ()) return v.bind(j.toArray ());
......@@ -473,21 +471,20 @@ struct QTransmogrifier<QCborValue> {
if (j.isInteger ()) return v.bind(j.toInteger ());
if (j.isDouble ()) return v.bind(j.toDouble ());
if (j.isString ()) return v.bind(j.toString ());
if (j.isByteArray()) return v.bind(j.toByteArray());
if (j.isNull ()) return v.null();
if (j.isUndefined() || j.isInvalid() || v->handleError(qBindUnexpectedValue)) return v.any();
return QValueStatus();
}
else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
if (j.isByteArray()) return v.bind(j.toByteArray());
if (j.isNull ()) return v.null();
return (j.isUndefined() || v->handleError(qBindUnexpectedValue)) ? v.any() : QValueEnd();
}
else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
}
static QValueStatus zap(QValue&& v, QCborValue& j) {
static QValueEnd zap(QValue&& v, QCborValue& j) {
if (v->mode()==Write) {
return zap(std::move(v),std::move(j));
}
else if (v->mode()==Read) {
QValueStatus r;
auto suspended = v->setErrorHandler();
QCborArray a; if ((r = v.bind(a))) { j = a ; v->setErrorHandler(suspended); return r; }
}
else if (v->mode()==Read) {
QValueEnd r;
auto suspended = v->setErrorHandler();
QCborArray a; if ((r = v.bind(a))) { j = a ; v->setErrorHandler(suspended); return r; }
QCborMap o; if ((r = v.bind(o))) { j = o ; v->setErrorHandler(suspended); return r; }
QString t; if ((r = v.bind(t))) { j = QCborValue( t ); v->setErrorHandler(suspended); return r; }
bool b; if ((r = v.bind(b))) { j = QCborValue( b ); v->setErrorHandler(suspended); return r; }
......@@ -496,18 +493,18 @@ struct QTransmogrifier<QCborValue> {
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( nullptr ); v->setErrorHandler(suspended); return r; }
v->setErrorHandler(suspended);
if ((r = v.any()) || v->handleError(qBindUnexpectedValue)) { j = QCborValue(); }
return r;
}
/**/ if ((r = v.null( ))) { j = QCborValue( nullptr ); v->setErrorHandler(suspended); return r; }
v->setErrorHandler(suspended);
if ((r = v.any())) { j = QCborValue(); }
return r;
}
else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
}
};
template<>
struct QTransmogrifier<QCborArray> {
static QValueStatus zap(QValue&& v, QCborArray&& j) {
static QValueEnd zap(QValue&& v, QCborArray&& j) {
if (v->mode()==Write) {
quint32 size=quint32(j.size());
auto s(v.sequence(&size));
......@@ -518,7 +515,7 @@ struct QTransmogrifier<QCborArray> {
}
else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
}
static QValueStatus zap(QValue&& v, QCborArray& j) {
static QValueEnd zap(QValue&& v, QCborArray& j) {
if (v->mode()==Write) {
return zap(std::move(v),std::move(j));
}
......@@ -538,7 +535,7 @@ struct QTransmogrifier<QCborArray> {
template<>
struct QTransmogrifier<QCborMap> {
static QValueStatus zap(QValue&& v, QCborMap&& j) {
static QValueEnd zap(QValue&& v, QCborMap&& j) {
if (v->mode()==Write) {
quint32 size=quint32(j.size());
auto r(v.record(&size));
......@@ -553,7 +550,7 @@ struct QTransmogrifier<QCborMap> {
}
else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
}
static QValueStatus zap(QValue&& v, QCborMap& j) {
static QValueEnd zap(QValue&& v, QCborMap& j) {
if (v->mode()==Write) {
return zap(std::move(v),std::move(j));
}
......
......@@ -62,15 +62,14 @@ public:
QValueMode mode() const noexcept { return QValueMode::Write; }
// Shortcuts
QValue value ( ) { return QValueStatus(this).value(); }
QSequence sequence(quint32* s=nullptr) { return QValueStatus(this).value().sequence(s); }
QValue value ( ) { return QCur(this).value(); }
QSequence sequence(quint32* s=nullptr) { return QCur(this).value().sequence(s); }
template<typename T> QValueStatus zap(T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
template<typename T> QValueEnd zap(T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
protected:
friend class QValueStatus;
friend class QCur;
bool trySequence(quint32* s=nullptr) { if (s) *io << *s; return true; }
bool tryRecord (quint32* s=nullptr) { if (s) *io << *s; return true; }
bool tryAny ( ) { return handleError(qBindUnexpectedValue); }
bool tryNull ( ) { *io << nullptr; return true; }
bool tryBind ( QUtf8DataView u) { QByteArray ba = QByteArray::fromRawData(u.data(), u.size()+int(sizeof('\0'))); return static_cast<QAbstractValue*>(this)->tryBind(ba); }
......
......@@ -60,9 +60,9 @@ public:
void reset(QJsonValue* v) { json=v; Q_ASSERT(v); steps.resize(0); }
// Shortcuts
/**/ QValue value ( ) { return QValueStatus(this).value(); }
/**/ QSequence sequence(quint32* s=nullptr) { return QValueStatus(this).value().sequence(s); }
template<typename T> QValueStatus bind ( T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
/**/ QValue value ( ) { return QCur(this).value(); }
/**/ QSequence sequence(quint32* s=nullptr) { return QCur(this).value().sequence(s); }
template<typename T> QValueEnd bind ( T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
protected:
friend class QJsonReader; //!< Calls methods below for out-of-order cachedItems
bool trySequence(quint32* s=nullptr) { Q_UNUSED(s); steps.push(Step( )); return true; }
......@@ -115,13 +115,12 @@ public:
}
// Shortcuts
/**/ QValue value ( ) { return QValueStatus(this).value(); }
/**/ QSequence sequence(quint32* s=nullptr) { return QValueStatus(this).value().sequence(s); }
template<typename T> QValueStatus bind ( T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
/**/ QValue value ( ) { return QCur(this).value(); }
/**/ QSequence sequence(quint32* s=nullptr) { return QCur(this).value().sequence(s); }
template<typename T> QValueEnd bind ( T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
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 tryAny ( ) { if (current().isUndefined()) { return true; } handleError(qBindUnexpectedValue ); return false; }
bool tryNull ( ) { if (current().isNull() ) { 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; }
......@@ -136,6 +135,7 @@ protected:
bool tryItem( ) { steps.last().idx++; return !(steps.last().item = current(1).toArray (). at(steps.last().idx )).isUndefined(); }
bool tryOut ( ) { steps.removeLast(); return true; }
bool tryAny() { return true; }
bool isValid() const noexcept { return true; }
bool handleError(QIdentifierLiteral e, QString context = QString()) { return errorHandler ? errorHandler(e, QString(currentPath().latin1()).append(context)) : false; }
private:
......@@ -164,15 +164,15 @@ public:
}
// Shortcuts
/**/ QValue value ( ) { return QValueStatus(this).value(); }
/**/ QSequence sequence(quint32* s=nullptr) { return QValueStatus(this).value().sequence(s); }
template<typename T> QValueStatus bind ( T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
/**/ QValue value ( ) { return QCur(this).value(); }
/**/ QSequence sequence(quint32* s=nullptr) { return QCur(this).value().sequence(s); }
template<typename T> QValueEnd bind ( T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
protected:
template<class T> friend class QModelWriter;
bool trySequence(quint32* s=nullptr) { Q_UNUSED(s); levels.push(Step{"","]"}); return putChar('['); }
bool tryRecord (quint32* s=nullptr) { Q_UNUSED(s); levels.push(Step{"","}"}); return putChar('{'); }
bool tryAny ( ) { return handleError(qBindUnexpectedValue) && write("undefined"); }
bool tryAny ( ) { return write("undefined"); } // TODO || (isStrictJson && handleError(qBindUnexpectedValue) && write("null");
bool tryNull ( ) { return write("null"); }
bool tryBind ( QUtf8DataView u) { return putString(u.data(), u.size()); }
// JSON literals
......@@ -246,13 +246,13 @@ public:
}
struct Step { int index; const char* end; QMap<QIdentifier,QJsonValue/*TODO QVariant for meta() support*/> cachedItems; Step(int i=-1, const char* e=nullptr) : index(i), end(e) {} };
struct Error { QIdentifierLiteral error; int line; int column; int index; QValueStatus zap(QValue&& value) { QByteArray u(error.utf8()); u.append(' ').append(QByteArray::number(line)).append(':').append(QByteArray::number(column)); return value.bind(QUtf8Data(u)); } };
struct Error { QIdentifierLiteral error; int line; int column; int index; QValueEnd zap(QValue&& value) { QByteArray u(error.utf8()); u.append(' ').append(QByteArray::number(line)).append(':').append(QByteArray::number(column)); return value.bind(QUtf8Data(u)); } };
QVector<Error> errors;
// Shortcuts
/**/ QValue value ( ) { return QValueStatus(this).value(); }
/**/ QSequence sequence(quint32* s=nullptr) { return QValueStatus(this).value().sequence(s); }
template<typename T> QValueStatus bind ( T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
/**/ QValue value ( ) { return QCur(this).value(); }
/**/ QSequence sequence(quint32* s=nullptr) { return QCur(this).value().sequence(s); }
template<typename T> QValueEnd bind ( T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
protected:
enum CachedNumber : quint8 { None=0, Integer, FloatingPoint };
......@@ -349,7 +349,7 @@ protected:
cachedValue = levels.last().cachedItems.take(QIdentifier(u));
cacheReader.reset(&cachedValue);
Q_ASSERT(!cacheLevel);
caching = &cacheReader; // let outer QValueStatus drive QJsonReader depending on bound T
caching = &cacheReader; // let outer QCur drive QJsonReader depending on bound T
return true;
}
else if (!tryItem(s)) { // record() end reached
......@@ -358,10 +358,10 @@ protected:
else if (u == s ) {
return true ;
}
else { // read cachedValue using a dedicated QValueStatus and QTransmogrifier<QValue> accepting value of any 'shape' (outer QTransmogrifier is specialized on outer T which is not usually generic enough)
else { // read cachedValue using a dedicated QCur and QTransmogrifier<QValue> accepting value of any 'shape' (outer QTransmogrifier is specialized on outer T which is not usually generic enough)
cachedValue = QJsonValue();
cacheWriter.reset(&cachedValue);
if (!QValueStatus(this).value().bind(QValueStatus(&cacheWriter).value())) return false;
if (!QCur(this).value().bind(QCur(&cacheWriter).value())) return false;
levels.last().cachedItems.insert(s, cachedValue);
continue; // until !tryItem(s) or u==s
}
......@@ -529,7 +529,7 @@ private:
template<>
struct QTransmogrifier<QJsonValue> {
static QValueStatus zap(QValue&& v, QJsonValue&& j) {
static QValueEnd zap(QValue&& v, QJsonValue&& j) {
if (v->mode()==Write) {
if (j.isObject()) return v.bind(j.toObject());
if (j.isArray ()) return v.bind(j.toArray ());
......@@ -538,16 +538,16 @@ struct QTransmogrifier<QJsonValue> {
if (j.isString()) return v.bind(j.toString());
if (j.isNull ()) return v.null();
if (j.isUndefined() || v->handleError(qBindUnexpectedValue)) return v.any();
return QValueStatus();
return QValueEnd();
}
else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
}
static QValueStatus zap(QValue&& v, QJsonValue& j) {
static QValueEnd zap(QValue&& v, QJsonValue& j) {
if (v->mode()==Write) {
return zap(std::move(v),std::move(j));
}
else if (v->mode()==Read) {
QValueStatus r;
QValueEnd r;
auto suspended = v->setErrorHandler();
QJsonArray a; if ((r = v.bind(a))) { j = a ; v->setErrorHandler(suspended); return r; }
QJsonObject o; if ((r = v.bind(o))) { j = o ; v->setErrorHandler(suspended); return r; }
......@@ -567,7 +567,7 @@ struct QTransmogrifier<QJsonValue> {
template<>
struct QTransmogrifier<QJsonArray> {
static QValueStatus zap(QValue&& v, QJsonArray&& j) {
static QValueEnd zap(QValue&& v, QJsonArray&& j) {
if (v->mode()==Write) {
quint32 size=quint32(j.size());
auto s(v.sequence(&size));
......@@ -578,7 +578,7 @@ struct QTransmogrifier<QJsonArray> {
}
else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
}
static QValueStatus zap(QValue&& v, QJsonArray& j) {
static QValueEnd zap(QValue&& v, QJsonArray& j) {
if (v->mode()==Write) {
return zap(std::move(v),std::move(j));
}
......@@ -598,7 +598,7 @@ struct QTransmogrifier<QJsonArray> {
template<>
struct QTransmogrifier<QJsonObject> {
static QValueStatus zap(QValue&& v, QJsonObject&& j) {
static QValueEnd zap(QValue&& v, QJsonObject&& j) {
if (v->mode()==Write) {
quint32 size=quint32(j.size());
auto s(v.record(&size));
......@@ -610,7 +610,7 @@ struct QTransmogrifier<QJsonObject> {
}
else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
}
static QValueStatus zap(QValue&& v, QJsonObject& j) {
static QValueEnd zap(QValue&& v, QJsonObject& j) {
if (v->mode()==Write) {
return zap(std::move(v),std::move(j));
}
......
......@@ -59,7 +59,7 @@ class QModelBind : public QAbstractValue
public:
QModelBind(QAbstractItemModel* m, bool rowFirst=true) : m(m), rowFirst(rowFirst) { Q_ASSERT(m); }
QValue value() { return QValueStatus(this).value(); }
QValue value() { return QCur(this).value(); }
protected:
enum : int {
T=0, //!< initial state
......
......@@ -52,12 +52,12 @@ class QSettingsWriter : public QAbstractValueWriter
{
Q_DISABLE_COPY(QSettingsWriter)
public:
QSettingsWriter(QSettings* s) : settings(s) { Q_ASSERT(s); levels.push(Level(qBindExpectedItem)); }
QSettingsWriter(QSettings* s) : settings(s) { Q_ASSERT(s); settings->beginGroup("test"); } // FIXME remove extra beginGroup
// Shortcuts
/**/ QValue value ( ) { return QValueStatus(this).value(); }
/**/ QSequence sequence(quint32* s=nullptr) { return QValueStatus(this).value().sequence(s); }
template<typename T> QValueStatus bind ( T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
/**/ QValue value ( ) { return QCur(this).value(); }
/**/ QSequence sequence(quint32* s=nullptr) { return QCur(this).value().sequence(s); }
template<typename T> QValueEnd bind ( T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
protected:
bool tryBind ( quint8&& t) { settings->setValue(key(), int(t) ); return true; }
bool tryBind ( quint16&& t) { settings->setValue(key(), int(t) ); return true; }
......@@ -69,16 +69,19 @@ protected:
bool tryAny () { settings->setValue(key(), QVariant() ); return true; }
bool tryNull() { settings->setValue(key(), QVariant::fromValue(nullptr)); return true; }
bool trySequence(quint32* =nullptr) { settings->beginGroup(key()); levels.push(Level( )); return true; }
bool tryRecord (quint32* =nullptr) { settings->beginGroup(key()); levels.push(Level(qBindExpectedItem)); return true; }
bool trySequence(quint32* =nullptr) { levels.push(Level( )); settings->beginGroup(key()); return true; } // TODO use beginWriteArray
bool tryRecord (quint32* =nullptr) { levels.push(Level(qBindExpectedItem)); settings->beginGroup(key()); return true; }
bool tryItem(QIdentifier& n) { levels.last().key=n; return true; }
bool tryItem( ) { levels.last().idx++; return true; }
bool tryOut ( ) { levels.pop(); settings->endGroup(); return true; }
bool tryOut ( ) {
levels.pop();
settings->endGroup();
return true;
}
private:
QString key() {
Q_ASSERT(!levels.isEmpty());
return levels.size()==1 ? QString("") :
return levels.size()==0 ? QString("") :
!levels.last().key.isNull()
? QString (levels.last().key.latin1())
: QString::number(levels.last().idx );
......@@ -108,7 +111,7 @@ public:
}
// Shortcuts
template<typename T> QValueStatus bind(T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
template<typename T> QValueEnd bind(T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
protected:
template<typename T>
bool tryBind(T& t) { return set(t, "Expected declared metatype T"); }
......@@ -132,13 +135,13 @@ protected:
bool trySequence(quint32* =nullptr) { if (levels.last().isGroup) { levels.push(Level( )); return true; } handleError(qBindExpectedSequence); return false; }
bool tryRecord (quint32* =nullptr) { if (levels.last().isGroup) { levels.push(Level(qBindExpectedItem)); return true; } handleError(qBindExpectedRecord ); return false; }
bool tryAny () { if (!settings->value(key()).isValid()) { return true; } handleError(qBindUnexpectedValue); return false; }
bool tryNull() { if ( settings->value(key()).isNull ()) { return true; } handleError(qBindExpectedNull ); return false; }
bool tryNull() { if ( settings->value(key()).isNull ()) { return true; } handleError(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 tryAny() { return true; }
bool isValid() const noexcept { return settings; }
bool handleError(QIdentifierLiteral error, QString context = QString()) { return errorHandler ? errorHandler(error, QString(currentPath().latin1()).append(context)) : false; }
private:
......
......@@ -27,4 +27,4 @@ QIdentifierLiteral qmChildren ("children" );
QIdentifierLiteral qmName ("name" );
QIdentifierLiteral qmColor ("color" );
QValue QValueStatus::value() noexcept { return QValue(std::move(*this)); }
QValue QCur::value() noexcept { return QValue(std::move(*this)); }
This diff is collapsed.
......@@ -56,9 +56,9 @@ public:
void reset(QVariant* v) { variant=v; Q_ASSERT(v); levels.resize(0); }
// Shortcuts
/**/ QValue value ( ) { return QValueStatus(this).value(); }
/**/ QSequence sequence(quint32* s=nullptr) { return QValueStatus(this).value().sequence(s); }
template<typename T> QValueStatus bind ( T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
/**/ QValue value ( ) { return QCur(this).value(); }