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 39a55e64 authored by EXT Arnaud Clère's avatar EXT Arnaud Clère
Browse files

Renamed QValueStatus now it is mostly hidden and taking into account it

may be templated on QValueMode...
parent e2e2a17d
......@@ -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,9 +245,9 @@ 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; }
......@@ -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;
}
......@@ -476,7 +476,7 @@ struct QTransmogrifier<QCborValue> {
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();
return QCur();
}
else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
}
......@@ -485,7 +485,7 @@ struct QTransmogrifier<QCborValue> {
return zap(std::move(v),std::move(j));
}
else if (v->mode()==Read) {
QValueStatus r;
QCur 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; }
......
......@@ -62,12 +62,12 @@ 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> QValueEnd 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); }
......
......@@ -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,9 +115,9 @@ 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; }
......@@ -158,9 +158,9 @@ public:
~QJsonWriter() { while (!levels.isEmpty()) write(levels.takeLast().end); }
// 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;
......@@ -244,9 +244,9 @@ public:
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 };
......@@ -343,7 +343,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
......@@ -352,10 +352,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
}
......@@ -532,7 +532,7 @@ 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(); }
}
......@@ -541,7 +541,7 @@ struct QTransmogrifier<QJsonValue> {
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; }
......
......@@ -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
......
......@@ -55,9 +55,9 @@ public:
QSettingsWriter(QSettings* s) : settings(s) { Q_ASSERT(s); levels.push(Level(qBindExpectedItem)); }
// 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; }
......@@ -108,7 +108,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"); }
......
......@@ -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)); }
......@@ -145,7 +145,7 @@ template<> struct BindSupport< QVariant> : BindNative {};
//#include <QtCore/qdatetime.h>
//#include <QtCore/quuid.h>
//! Interface for QValueStatus with a fixed subset of BindNative types and just a few optional methods
//! Interface for QValue implementations with a fixed subset of BindNative types and just a few optional methods
struct QAbstractValue {
virtual ~QAbstractValue() = default;
......@@ -237,35 +237,47 @@ template<class T_> class QVal;
#include <type_traits>
template<class T> using RemoveCvRef = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
struct QValueEnd { bool isValid = false; };
//! A QValueEnd represents the end of a QValue traversal and processing
//!
//! \warning isValid does not take into account intermediate errors that are handled by QValueErrorHandler
//!
class QValueEnd {
public:
QValueEnd() = default;
QValueEnd(bool result) : status(result) {}
bool isValid() { return status; }
explicit operator bool() { return isValid(); }
private:
bool status = false;
};
//! A QValueStatus is a move-only, non-owning pointer to QAbstractValue
//! A QCur is a cursor inside a structured value implemented as a move-only, non-owning pointer to QAbstractValue
//!
//! Its public interface only allows checking the status of resulting QAbstractValue operation(s) using operator bool() and to handleError().
//! Its protected interface is used by QVal, QSeq, QRec to only allow new QAbstractValue operation from the previous valid QValueStatus.
//! Its public interface only allows to check the status of QAbstractValue calls using explicit operator bool(), and to handleError().
//! Its protected interface is used by QVal, QSeq, QRec to only allow QAbstractValue calls from the previous valid QCur.
//!
//! It allows to easily add QTransmogrifier support to existing Reader/Writers by adding at least one value() method returning a QValueStatus(this).value()
//! It allows to easily add QTransmogrifier support to existing Reader/Writers by adding at least one value() method returning a QCur(this).value()
//! \see TextWriter in main.cpp
//!
//! \remark QValueStatus allows using the fluent interface without checking intermediate status by setting impl=nullptr on error
//! and subsequently bypassing impl calls. Errors can result from:
//! \remark QCur allows using a fluent interface without checking intermediate status by setting impl=nullptr on error
//! and subsequently bypassing structured value impl calls. Errors can result from:
//! - using the same intermediate QVal/QSeq/QRec twice (a programmer error), or
//! - runtime impl errors like trying to bind a trySequence() whereas the data read matches a tryRecord()
//!
class QValueStatus
class QCur
{
Q_DISABLE_COPY(QValueStatus)
Q_DISABLE_COPY(QCur)
public:
QValueStatus ( ) noexcept : impl(nullptr) {}
QValueStatus (QValueStatus&& o) noexcept : impl(o.impl) { o.impl = nullptr; }
QValueStatus& operator=(QValueStatus&& o) noexcept { auto prev = impl; impl = o.impl; o.impl = prev; return *this; }
QCur ( ) noexcept : impl(nullptr) {}
QCur (QCur&& o) noexcept : impl(o.impl) { o.impl = nullptr; }
QCur& operator=(QCur&& o) noexcept { auto prev = impl; impl = o.impl; o.impl = prev; return *this; }
explicit QValueStatus(QAbstractValue* i) : impl(i) { Q_ASSERT(impl); }
explicit QCur(QAbstractValue* i) : impl(i) { Q_ASSERT(impl); }
QValueMode mode() const noexcept { return impl ? impl->mode() : QValueMode::Invalid; }
explicit operator bool() const noexcept { return impl && impl->isValid(); } //!< Drives QTransmogrifier<T>::bind() traversal
QValueStatus* operator ->() noexcept { return this; }
QVal<QValueStatus> value() noexcept ;
QCur* operator ->() noexcept { return this; }
QVal<QCur> value() noexcept ;
operator QValueEnd() { return QValueEnd{impl!=nullptr}; }
QValueErrorHandler setErrorHandler(QValueErrorHandler newHandler = nullptr) { return Q_LIKELY(impl) ? impl->setErrorHandler(newHandler) : nullptr; }
......@@ -297,7 +309,7 @@ private:
bool tryItem( ) { return Q_LIKELY(impl) && impl->tryItem( ); }
bool tryOut ( ) { return Q_LIKELY(impl) && impl->tryOut ( ); }
private:
QValueStatus _unsafeCopy() noexcept { return impl ? QValueStatus(impl) : QValueStatus(); }
QCur _unsafeCopy() noexcept { return impl ? QCur(impl) : QCur(); }
QAbstractValue* impl = nullptr;
};
......@@ -315,9 +327,9 @@ template<class T_> class QRec; //!< a Record data structure defined below
template<class T_> class QSeq; //!< a Sequence data structure defined below
template<class T_> class QVal; //!< a choice of sequence(), record(), null(), or values with at least a textual representation and possibly binary ones
using QValue = QVal<QValueStatus>;
using QSequence = QSeq<QValueStatus>;
using QRecord = QRec<QValueStatus>;
using QValue = QVal<QCur>;
using QSequence = QSeq<QCur>;
using QRecord = QRec<QCur>;
// Custom bind support
......@@ -327,8 +339,8 @@ using QRecord = QRec<QValueStatus>;
template<typename T> using QValFunction = QValueEnd(*)(T &,QValue &&) ;
template<class Ts> using QSeqFunction = QSequence(*)(Ts&,QSequence&&) ;
#endif
/**/ using QValLambda = std::function<QValueEnd(QValue &&)>;
/**/ using QSeqLambda = std::function<QSequence(QSequence&&)>;
using QValLambda = std::function<QValueEnd(QValue &&)>;
using QSeqLambda = std::function<QSequence(QSequence&&)>;
template<class T_> class QVal
{
......@@ -338,7 +350,7 @@ public:
explicit QVal(T_&& out) noexcept { std::swap(outer, out); }
explicit operator bool() const noexcept { return outer.operator bool(); } //!< Drives QTransmogrifier<T>::bind() traversal
QValueStatus* operator->() noexcept { return outer.operator ->(); }
QCur* operator->() noexcept { return outer.operator ->(); }
QVal<T_> meta (QIdentifierLiteral& n, QAsciiData& m) { outer->_meta(n,m); return std::move(*this); }
......@@ -357,8 +369,8 @@ public:
template<typename T> T_ bind(T& t, T&& defaultT) { return outer->tryBind(QDefaultValue<T>{t,defaultT}) ? std::move(outer) : T_(); }
// Custom bind support
/**/ T_ with( QValLambda customBind) { return customBind( std::move(unsafeThis())).isValid ? std::move(outer) : T_(); }
template<typename T> T_ with(T& t, QValFunction<T> customBind) { return customBind(t, std::move(unsafeThis())).isValid ? std::move(outer) : T_(); }
/**/ T_ with( QValLambda customBind) { return customBind( std::move(unsafeThis())).isValid() ? std::move(outer) : T_(); }
template<typename T> T_ with(T& t, QValFunction<T> customBind) { return customBind(t, std::move(unsafeThis())).isValid() ? std::move(outer) : T_(); }
// Literal metadata support
QVal<T_> meta(const char* n, const char* m) { return meta(QIdentifierLiteral(n),QAsciiData(m)); }
......@@ -376,7 +388,7 @@ public:
private:
QValue unsafeThis() noexcept { return QValue(outer->_unsafeCopy()); }
T_ outer = T_(); //!< moved context of current traversal up to QValueStatus that will point to the value itself (be it a QIODevice or QCborValue)
T_ outer = T_(); //!< moved context of current traversal up to QCur that will point to the value itself (be it a QIODevice or QCborValue, ...)
};
template<class T_> class QSeq
......@@ -387,7 +399,7 @@ public:
explicit QSeq(T_&& out) noexcept { std::swap(outer, out); }
explicit operator bool() const noexcept { return outer.operator bool(); } //!< Drives QTransmogrifier<T>::bind() traversal
QValueStatus* operator->() noexcept { return outer.operator ->(); }
QCur* operator->() noexcept { return outer.operator ->(); }
operator QValueEnd() { return out(); /* calls T_::operator QValueEnd() if T_ != QValueEnd */ }
/**/ T_ out() { return outer-> tryOut() ? std::move(outer) : T_ (); }
......@@ -411,8 +423,8 @@ public:
// Custom bind support
/**/ QSeq<T_> with ( QSeqLambda customBind) { return bool(customBind( std::move(unsafeThis()))) ? std::move(*this) : QSeq<T_>(); }
template<class Ts> QSeq<T_> with (Ts& ts, QSeqFunction<Ts> customBind) { return bool(customBind(ts, std::move(unsafeThis()))) ? std::move(*this) : QSeq<T_>(); }
template<class Ts> QSeq<T_> forEach (Ts& ts, QValueStatus(*)(typename Ts::value_type&, QValue&&)
, bool (*)(const typename Ts::value_type&) = [](const typename Ts::value_type&) { return true; });
template<class Ts> QSeq<T_> forEach (Ts& ts, QValueEnd(*)(typename Ts::value_type&, QValue&&)
, bool(*)(const typename Ts::value_type&) = [](const typename Ts::value_type&) { return true; });
// Shortcut
template<typename T> QSeq<T_> operator<<(T&& t) { return item().bind(std::forward<T>(t)); } // stream compatible
......@@ -432,7 +444,7 @@ public:
explicit QRec(T_&& out) noexcept { std::swap(outer, out); }
explicit operator bool() const noexcept { return outer.operator bool(); } //!< Drives QTransmogrifier<T>::bind() traversal
QValueStatus* operator->() noexcept { return outer.operator ->(); }
QCur* operator->() noexcept { return outer.operator ->(); }
operator QValueEnd() { return out(); /* calls T_::operator QValueEnd() if T_ != QValueEnd */ }
/**/ T_ out ( ) { return outer->tryOut ( ) ? std::move(outer) : T_ (); }
......@@ -463,18 +475,14 @@ private:
T_ outer = T_();
};
//! QTransmogrifier is a class of template methods binding T parts with QValue obeying a simple generic data model, and returning the QValueStatus
//! QTransmogrifier is a class of template methods binding T parts with QValue obeying a simple generic data model, and returning a QValueEnd
//! Each QTransmogrifier<_,T> should provide bind(T&&) and bind(T&) overloads for convenience and may provide bind(const T&) for performance depending on T
//!
//! Its least specialized definition calls a T::bind(QValue&&) method that is more convenient to define than a QTransmogrifier specialization
//! \see Person::bind() definition in main.cpp for an example
//!
//! \remark bind is not a template function to avoid conflicts with ADL and allow more precise class specialization
//! \remark QVal<> is a move-only type passed by rvalue reference following http://scottmeyers.blogspot.com/2014/07/should-move-only-types-ever-be-passed.html
//! \remark bind is more explicit than operator() but could be changed to enable use as std::function
//! \remark QTransmogrifier has a structure similar to a Traversable : https://wiki.haskell.org/Typeclassopedia#Traversable
//! A QTraverse<QValue,T> would be equivalent but would not mandate to actually traverse T to get from QValue to the actual QValueStatus
//! A QFold<QValueStatus,T> would be equivalent to a Foldable allowing to fold T to any QValueStatus without mandating a common QVal/QSeq structure
//! \remark QValue is a move-only type passed by rvalue reference following http://scottmeyers.blogspot.com/2014/07/should-move-only-types-ever-be-passed.html
//!
template<typename T, typename TEnabledIf=void>
struct QTransmogrifier {
......@@ -484,7 +492,7 @@ struct QTransmogrifier {
};
template<class T>
bool QValueStatus::tryBind(BindGeneric, T&& t) { return QTransmogrifier<RemoveCvRef<T>>::zap(QValue(_unsafeCopy()),std::forward<T>(t)).isValid; }
bool QCur::tryBind(BindGeneric, T&& t) { return QTransmogrifier<RemoveCvRef<T>>::zap(QValue(_unsafeCopy()),std::forward<T>(t)).isValid(); }
// //////////////////////////////////////////////////////////////////////////
// Base QAbstractValue implementations for Read and Write QValueMode
......@@ -785,13 +793,13 @@ protected:
#include <QtCore/qatomic.h>
class QLogger : public QValueStatus
class QLogger : public QCur
{
static QAtomicPointer<QAbstractValueWriter> s_impl;
public:
static QAbstractValueWriter* setWriter(QAbstractValueWriter* i) { return s_impl.fetchAndStoreOrdered(i); }
QLogger() : QValueStatus(s_impl) {}
QLogger() : QCur(s_impl) {}
};
// TODO in .cpp QAtomicPointer<QAbstractValueWriter> QLogger::s_impl = nullptr;
......@@ -803,7 +811,7 @@ public:
//! \warning copying should be forced using QBindable(T(t)) if QBindable::bind() must be called after t is destructed like when queued to another thread for writing
class QBindable
{
std::function<QValueStatus(QValue&&)> f;
QValLambda f;
public:
QBindable() {}
template<typename T> QBindable(T& t) : f(/*captured T ref */[&t] (QValue&& v) { return v.bind(t); } ) {}
......@@ -818,7 +826,7 @@ public:
template<class T_>
template<class Ts>
QSeq<T_> QSeq<T_>::forEach(Ts& ts,
QValueStatus(*itemBind)(typename Ts::value_type&, QValue&&),
QValueEnd(*itemBind)(typename Ts::value_type&, QValue&&),
bool(*itemPredicate)(const typename Ts::value_type&)) {
if ((*this)->mode()==Write) {
for (auto&& t : ts) {
......@@ -868,7 +876,7 @@ QValueEnd qMetaZap(QValue&& v, T* t) {
r = r.item(id).bind(pv);
if (!r || !pv.isValid() || pv.isNull()) {
if (p.isResettable() && !p.resetOnGadget(t) && !v->handleError(qBindIgnoredItem)) {
return QValueStatus();
return QValueEnd();
}
}
else {
......@@ -876,14 +884,14 @@ QValueEnd qMetaZap(QValue&& v, T* t) {
pv.convert(QVariant::Int);
}
if (!p.writeOnGadget(t, pv) && !v->handleError(qBindIgnoredItem)) {
return QValueStatus();
return QValueEnd();
}
}
}
else if (rw==Write) {
QVariant pv = p.readOnGadget(t);
if (!pv.isValid() && !v->handleError(qBindIgnoredItem)) {
return QValueStatus();
return QValueEnd();
}
else {
QIdentifier id(p.name());
......@@ -995,7 +1003,7 @@ struct QTransmogrifier<T[Size]> {
QVal<QSequence> i; unsigned j=0;
while ((i = s.item())) {
if (j== Size && !i->handleError(qBindIgnoredItem)) {
return QValueStatus();
return QValueEnd();
}
T t;
if((s = i.bind(t))) // gives back control to s, enabling the next s.item() call
......@@ -1145,7 +1153,6 @@ template<> //!< \remark Enumerating types where BindSupport<_> = BindNative with
struct QTransmogrifier<QValue> {
static QValueEnd zap(QValue&& src, QValue&& dst) {
if (src->mode()==Read && dst->mode()==Write) {
QValueStatus srcRes;
auto suspended = src->setErrorHandler();
QSequence srcSeq; QSequence dstSeq;
if ( (srcSeq = src.sequence())) {
......@@ -1173,30 +1180,31 @@ struct QTransmogrifier<QValue> {
/**/ dstRec.out();
return srcRec.out();
}
QUtf8Data u; if ((srcRes = src.bind( u))) { dst.bind( u); src->setErrorHandler(suspended); return srcRes; } //! \remark QUtf8Data has common valus with QString but is more precise and efficient
QString t; if ((srcRes = src.bind( t))) { dst.bind( t); src->setErrorHandler(suspended); return srcRes; }