Commit b345b8cf authored by EXT Arnaud Clère's avatar EXT Arnaud Clère
Browse files

Partially renamed as in Qt proposal

parent b59d09c6
# Design
The core QBind implementation (excluding IBind implementations) is a few hundreds line of C++11 using templates defined
in the headers, an abstract IBind class, and IWriter/IReader base classes.
The core QBind implementation (excluding QAbstractValue implementations) is a few hundreds line of C++11 using templates defined
in the headers, an abstract QAbstractValue class, and QAbstractValueWriter/QAbstractValueReader base classes.
## The key idea
......@@ -16,7 +16,7 @@ another generic dataset, binding the related parts together. In effect:
Hence, from now on, we will use the term *bind* instead of the more restricted *(de)serialization* term.
This traversal is driven by QBind<T> methods which may use a BindMode (Read,Write,...) to determine whether to read the generic dataset or write it according to the C++ one.
This traversal is driven by QBind<T> methods which may use a QValueMode (Read,Write,...) to determine whether to read the generic dataset or write it according to the C++ one.
[^1]: *traverse* meaning to go through without returning back
......@@ -46,42 +46,42 @@ ways of representing data structures such as XML (e.g. binding the Person type w
The QBind traversal is formally described by the following recursive automaton:
```mermaid
graph LR
subgraph Val
subgraph QVal
i((start))--"null()" --> x((end))
i((start))--"bind#lt;T>()"--> x((end))
i((start))--"sequence()" --> Seq
i((start))--"record()" --> Rec
Seq --"out()" --> x((end))
Rec --"out()" --> x((end))
Seq --"item()" --> vs["Val#lt;Seq>"]
Rec --"item(name)" --> vr["Val#lt;Rec>"]
i((start))--"sequence()" --> QSeq
i((start))--"record()" --> QRec
QSeq --"out()" --> x((end))
QRec --"out()" --> x((end))
QSeq --"item()" --> vs["QVal#lt;QSeq>"]
QRec --"item(name)" --> vr["QVal#lt;QRec>"]
end
```
- Boxes (nested) represent possible states when traversing the data, the automaton is always in a single valid state
- Edges represent possible state transitions that translate to specific data format read/write actions
The automaton is implemented as follows:
- `Val<_>`, `Rec<_>` and `Seq<_>` types implement possible states where _ denotes the type of outer state
- `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 `Val<Cursor>` and the final state is `Cursor` (for instance: `Val<Cursor>::null()` returns a `Cursor`)
- Returning a `Rec<_>` or `Seq<_>` type automatically convert to the final `Cursor` type invoking as much `out()` transitions as required
- `Cursor` is a non-owning pointer to an `IBind` interface which implementations translate data traversal into specific
- 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
data format read/write actions
- `Cursor` instance is moved from the start state type to the end state type only for successful transitions, allowing to test
- `QValueStatus` 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 `IBind` implementations:
- 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 `Cursor` that transparently bypasses calls to `IBind`
- `bind<T>()` calls are forwarded to the actual `IBind` or generic `QBind` depending on `BindSupport<T>`:
- BindNative : **IBind** interface method
- In case of unsuccessfull transition the returned state type receives a null `QValueStatus` that transparently bypasses calls to `QAbstractValue`
- `bind<T>()` calls are forwarded to the actual `QAbstractValue` or generic `QBind` depending on `BindSupport<T>`:
- BindNative : **QAbstractValue** interface method
- BindGeneric : **QBind** template specialization for T
- Every `bind<T>()` starts from a Val<Cursor> which is an un *unsafe* Cursor copy wrt well-formedness (these `unsafeItem()` copies are protected from incorrect use)
- 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)
## C++ types extensibility
QBind is a functor templated on T type receiving a Value and T reference (either lvalue or rvalue reference) and returning the Cursor.
QBind is a functor templated on T type receiving a Value and T reference (either lvalue or rvalue reference) and returning the QValueStatus.
Template specializations can be defined for any T and optionally refined for specific Cur<TImpl> with different sets of BindNative types.
A default QBind specialization attempts to call `T::bind(...)` to conveniently bind `T* this` without having to understand template syntax,
......@@ -95,35 +95,35 @@ 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 Cursor from a `Val<Cursor>` is to traverse
the data without backtracking, calling only and all necessary IBind virtual methods.
Thanks to this design, the compiler will make sure that the only possibility to return a QValueStatus 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 Cursor.
The downside is that writing loops with the fluent interface is unnatural as one must never forget to follow the valid QValueStatus.
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 Cursor and automatically ignored
seq = seq.bind(t); // do not forget to reassign seq, or subsequent items will be `bind` to the moved-from QValueStatus and automatically ignored
}
```
## Write performance
Since `Cursor`, `Val`, `Rec` and `Seq` have no data member other than outer types and `IBind*`, calling their methods can be
Since `QValueStatus`, `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 IBind pointer validity [^1]
2. call the IBind virtual method corresponding to the possible transitions
3. return the resulting Cursor, Val, Rec or Seq with a valid or invalid Cursor depending on IBind method success or failure
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
[^1]: Experiments to use constexpr to bypass this step for writers that always return true did not seem to improve performance.
`QBind<T>` can define up to 3 bind() overloads to efficiently and conveniently handle lvalue references, const lvalue references, and
rvalue references depending on T characteristics (which of copy/move is 1/possible and 2/efficient).
Compared to manually calling non-virtual, format-specific implementations, the overhead of always testing the validity of IBind*
Compared to manually calling non-virtual, format-specific implementations, the overhead of always testing the validity of QAbstractValue*
and calling virtual methods is around 20% in our benchmark, with a maximum of 100% for trivial format-specific implementations
like copying a single char to a pre-allocated buffer.
......@@ -135,31 +135,31 @@ Other than that, write performance depends on several factors:
distinguishable from QByteArray binary data
- Using QData<TContent> classes to tag string encodings allowed to pinpoint unnecessary encoding conversions, notably in QVariant handling
- In the end, directly using QByteArray buffers instead of using QIODevice can amount to ~ 2x better write performance
- IBind implementations need to use efficient data structures from storing state. For instance, using an optimized std::vector<bool>
- QAbstractValue implementations need to use efficient data structures from storing state. For instance, using an optimized std::vector<bool>
to memorize opened JSON object/array can usually be stored in a single byte and avoid memory allocations, resulting in ~ 10x better
write performance compared to QVector<bool>
## Read robustness
Performance is not so important for Read. But compared to manually calling non-virtual, format-specific implementations, QBind
enforces well-formedness checks necessary to reliably read data coming from unknown sources (IBind implementations being responsible
enforces well-formedness checks necessary to reliably read data coming from unknown sources (QAbstractValue implementations being responsible
for low-level checks).
All errors are reported as `QIdentifierLiteral` to the `IBind` implementations that will decide what to do with them:
- ignore the details and set a global error status enumeration is usually appropriate to IWriter implementations
- storing all read mismatches is usually more appropriate to world-facing IReader implementations
All errors are reported as `QIdentifierLiteral` to the `QAbstractValue` implementations that will decide what to do with them:
- ignore the details and set a global error status enumeration is usually appropriate to QAbstractValueWriter implementations
- storing all read mismatches is usually more appropriate to world-facing QAbstractValueReader implementations
Standardizing error literals allows efficient reporting and analysis while ensuring that various libraries can define new ones independantly.
## Data types extensibility
IBind is an abstract base class for translating fluent interface calls to format-specific read or write actions.
The fluent interface guarantees that IBind virtual methods are always called at appropriate times, so IBind implementations do not
QAbstractValue is an abstract base class for translating fluent interface calls to format-specific read or write actions.
The fluent interface guarantees that QAbstractValue virtual methods are always called at appropriate times, so QAbstractValue implementations do not
have to check well-formedness. It defines a set of BindNative types and default textual representations and encodings for non-textual
native types simplifying again the implementations (see TextWriter example).
IWriter and IReader provide partial IBind implementations simplifying the work of implementors and offering default textual representations.
QAbstractValueWriter and QAbstractValueReader provide partial QAbstractValue implementations simplifying the work of implementors and offering default textual representations.
*NB:* BindNative types could be extended by specifying BindSupport<T> trait but then, (de)serialization code must be specialized
for each TImpl. For instance, a QBind<QColor,QDataStream> may be implemented differently from QBind<QColor,IBind> but QBind<QColor>
for each TImpl. For instance, a QBind<QColor,QDataStream> may be implemented differently from QBind<QColor,QAbstractValue> but QBind<QColor>
example shows that meta() can also be used to avoid specialized serialization code that breaks RW2 requirement. If meta() is deemed
enough, the BindSupport trait and TImpl template parameters can be removed.
......@@ -25,4 +25,4 @@ QIdentifierLiteral qmChildren ("children" );
QIdentifierLiteral qmName ("name" );
QIdentifierLiteral qmColor ("color" );
Val<Cursor> Cursor::value() noexcept { return Val<Cursor>(std::move(*this)); }
QValue QValueStatus::value() noexcept { return QValue(std::move(*this)); }
This diff is collapsed.
This diff is collapsed.
......@@ -8,10 +8,10 @@ bool AsciiIdentifier::isValid(const char* s, int size)
if (s[i] < 0x20/*' '*/ || 0x7F/*DEL*/ == s[i])
return false;
if (i==0 && (s[i]!='_' && (s[0]<'A' || 'z'<s[0] || ('Z'<s[0] && s[0]<'a'))))
qWarning("Non alphabetic first character may not be supported by IBind");
qWarning("Non alphabetic first character may not be supported by QAbstractValue");
if (i< 0 && (s[i]!='_' && (s[i]<'0' || 'z'<s[i] || ('9'<s[i] && s[i]<'A')
|| ('Z'<s[i] && s[i]<'a'))))
qWarning("Non alphanumeric or underscore character may not be supported by IBind");
qWarning("Non alphanumeric or underscore character may not be supported by QAbstractValue");
}
return true;
}
......
......@@ -52,49 +52,49 @@
// TODO Use QByteArray directly
//! \warning As with QDataStream, QDataWriter and QDataReader must bind compatible C++ data types, and QDataStream ByteOrder, FloatingPointPrecision and Version
//! \remark When not statically known, such information can be transmitted using meta("type",...) although some IBind implementations may not support it
class QDataWriter : public IWriter // allows runtime flexibility but requires QBind<T> in addition to T::operator<<(QDataStream&) and does not warrant QDataStream operators compatibility if QBind<T> ignores qmDataStreamVersion in meta()
//! \remark When not statically known, such information can be transmitted using meta("type",...) although some QAbstractValue implementations may not support it
class QDataWriter : public QAbstractValueWriter // allows runtime flexibility but requires QBind<T> in addition to T::operator<<(QDataStream&) and does not warrant QDataStream operators compatibility if QBind<T> ignores qmDataStreamVersion in meta()
{
Q_DISABLE_COPY(QDataWriter)
public:
QDataWriter(QDataStream* io) : io(io) { Q_ASSERT(io); version = QAsciiData(QByteArray::number(io->version())); }
BindMode mode() const noexcept { return BindMode::Write; }
QValueMode mode() const noexcept { return QValueMode::Write; }
// Shortcuts
Val<Cursor> value ( ) { return Cursor(this).value(); }
Seq<Cursor> sequence(quint32* s=nullptr) { return Cursor(this).value().sequence(s); }
QValue value ( ) { return QValueStatus(this).value(); }
QSeq<QValueStatus> sequence(quint32* s=nullptr) { return QValueStatus(this).value().sequence(s); }
template<typename T> Cursor bind(T&& t) { return Cursor(this).value().bind(std::forward<T>(t)); }
template<typename T> QValueStatus bind(T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
protected:
friend class Cursor;
bool _sequence(quint32* s=nullptr) { if (s) *io << *s; return true; }
bool _record (quint32* s=nullptr) { if (s) *io << *s; return true; }
bool _null ( ) { *io << nullptr; return true; }
bool _bind ( QUtf8DataView u) { QByteArray ba = QByteArray::fromRawData(u.data(), u.size()+int(sizeof('\0'))); return static_cast<IBind*>(this)->_bind(ba); }
friend class QValueStatus;
bool trySequence(quint32* s=nullptr) { if (s) *io << *s; return true; }
bool tryRecord (quint32* s=nullptr) { if (s) *io << *s; return true; }
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); }
bool _bind ( QUtf8Data&& t) { *io << t.utf8(); return true; }
bool _bind ( QString&& t) { *io << t; return true; }
bool _bind ( bool&& t) { *io << t; return true; }
bool _bind ( qint8&& t) { *io << t; return true; }
bool _bind ( quint8&& t) { *io << t; return true; }
bool _bind ( qint16&& t) { *io << t; return true; }
bool _bind ( quint16&& t) { *io << t; return true; }
bool _bind ( qint32&& t) { *io << t; return true; }
bool _bind ( quint32&& t) { *io << t; return true; }
bool _bind ( qint64&& t) { *io << t; return true; }
bool _bind ( quint64&& t) { *io << t; return true; }
bool _bind ( float&& t) { *io << t; return true; }
bool _bind ( double&& t) { *io << t; return true; }
bool _bind ( QByteArray&& t) { *io << t; return true; }
bool _bind ( QVariant&& t) { *io << t; return true; }
bool tryBind ( QUtf8Data&& t) { *io << t.utf8(); return true; }
bool tryBind ( QString&& t) { *io << t; return true; }
bool tryBind ( bool&& t) { *io << t; return true; }
bool tryBind ( qint8&& t) { *io << t; return true; }
bool tryBind ( quint8&& t) { *io << t; return true; }
bool tryBind ( qint16&& t) { *io << t; return true; }
bool tryBind ( quint16&& t) { *io << t; return true; }
bool tryBind ( qint32&& t) { *io << t; return true; }
bool tryBind ( quint32&& t) { *io << t; return true; }
bool tryBind ( qint64&& t) { *io << t; return true; }
bool tryBind ( quint64&& t) { *io << t; return true; }
bool tryBind ( float&& t) { *io << t; return true; }
bool tryBind ( double&& t) { *io << t; return true; }
bool tryBind ( QByteArray&& t) { *io << t; return true; }
bool tryBind ( QVariant&& t) { *io << t; return true; }
bool _isOk() noexcept { return io && io->status()==QDataStream::Ok; }
bool _out ( ) { return true; }
bool _item( ) { return true; }
bool _item(QIdentifier& ) { return true; } // record keys are implicit, maps should be serialized as a sequence of records with key and value items
bool _item(QIdentifierLiteral) { return true; } // for QBaseWriter
bool tryOut ( ) { return true; }
bool tryItem( ) { return true; }
bool tryItem(QIdentifier& ) { return true; } // record keys are implicit, maps should be serialized as a sequence of records with key and value items
bool tryItem(QIdentifierLiteral) { return true; } // for QBaseWriter
void _meta(QIdentifierLiteral& n, QAsciiData& m) { if (n.isNull()) n=qmDataStreamVersion; if (n == qmDataStreamVersion) m=version; }
private:
......
This diff is collapsed.
This diff is collapsed.
......@@ -48,31 +48,31 @@
// //////////////////////////////////////////////////////////////////////////
// QBind<T,QVariant*> support for the fixed set of QSettingsWriter's BindNative types
class QSettingsWriter : public IWriter
class QSettingsWriter : public QAbstractValueWriter
{
Q_DISABLE_COPY(QSettingsWriter)
public:
QSettingsWriter(QSettings* s) : settings(s) { Q_ASSERT(s); levels.push(Level(qBindExpectedItem)); }
// Shortcuts
/**/ Val<Cursor> value ( ) { return Cursor(this).value(); }
/**/ Seq<Cursor> sequence(quint32* s=nullptr) { return Cursor(this).value().sequence ( s); }
template<typename T> Cursor bind ( T&& t) { return Cursor(this).value().bind(std::forward<T>(t)); }
/**/ QValue value ( ) { return QValueStatus(this).value(); }
/**/ QSeq<QValueStatus> 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)); }
protected:
bool _bind ( quint8&& t) { settings->setValue(key(), int(t) ); return true; }
bool _bind ( quint16&& t) { settings->setValue(key(), int(t) ); return true; }
bool _bind ( quint32&& t) { settings->setValue(key(), int(t) ); return true; }
bool _bind (QUtf8DataView u) { settings->setValue(key(), QString::fromUtf8(u.data(), u.size())); return true; }
bool _bind ( QStringView s) { settings->setValue(key(), s.toString() ); return true; }
bool _bind ( QString&& s) { settings->setValue(key(), s ); return true; }
bool _null ( ) { settings->setValue(key(), QVariant() ); return true; }
bool _sequence(quint32* =nullptr) { settings->beginGroup(key()); levels.push(Level( )); return true; }
bool _record (quint32* =nullptr) { settings->beginGroup(key()); levels.push(Level(qBindExpectedItem)); return true; }
bool _item(QIdentifier& n) { levels.last().key=n; return true; }
bool _item( ) { levels.last().idx++; return true; }
bool _out ( ) { levels.pop(); settings->endGroup(); return true; }
bool tryBind ( quint8&& t) { settings->setValue(key(), int(t) ); return true; }
bool tryBind ( quint16&& t) { settings->setValue(key(), int(t) ); return true; }
bool tryBind ( quint32&& t) { settings->setValue(key(), int(t) ); return true; }
bool tryBind (QUtf8DataView u) { settings->setValue(key(), QString::fromUtf8(u.data(), u.size())); return true; }
bool tryBind ( QStringView s) { settings->setValue(key(), s.toString() ); return true; }
bool tryBind ( QString&& s) { settings->setValue(key(), s ); return true; }
bool tryNull ( ) { settings->setValue(key(), QVariant() ); 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 tryItem(QIdentifier& n) { levels.last().key=n; return true; }
bool tryItem( ) { levels.last().idx++; return true; }
bool tryOut ( ) { levels.pop(); settings->endGroup(); return true; }
private:
QString key() {
Q_ASSERT(!levels.isEmpty());
......@@ -89,14 +89,14 @@ private:
// --------------------------------------------------------------------------
class QSettingsReader : public IReader
class QSettingsReader : public QAbstractValueReader
{
Q_DISABLE_COPY(QSettingsReader)
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; template<class T> T bind(Val<T>&& value) { return value.bind(QUtf8Data(error.utf8()+' '+path.utf8())); } };
struct Error { QIdentifierLiteral error; QAsciiData path; template<class T> T bind(QVal<T>&& value) { return value.bind(QUtf8Data(error.utf8()+' '+path.utf8())); } };
QVector<Error> errors;
QAsciiData currentPath() {
......@@ -109,34 +109,34 @@ public:
}
// Shortcuts
template<typename T> Cursor bind(T&& t) { return Cursor(this).value().bind(std::forward<T>(t)); }
template<typename T> QValueStatus bind(T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
protected:
template<typename T>
bool _bind(T& t) { return set(t, "Expected declared metatype T"); }
bool tryBind(T& t) { return set(t, "Expected declared metatype T"); }
bool _bind(QUtf8Data& t) { return set(t, qBindExpectedText ); }
bool _bind( QString& t) { return set(t, qBindExpectedText ); }
bool _bind( bool& t) { return set(t, qBindExpectedBoolean); }
bool _bind( QByteArray& t) { return set(t, qBindExpectedBytes ); }
bool tryBind(QUtf8Data& t) { return set(t, qBindExpectedText ); }
bool tryBind( QString& t) { return set(t, qBindExpectedText ); }
bool tryBind( bool& t) { return set(t, qBindExpectedBoolean); }
bool tryBind( QByteArray& t) { return set(t, qBindExpectedBytes ); }
// Convert numerical types to strictly larger ones // TODO convert all compatible values
bool _bind( qint8& t) { return set(t, qBindExpectedInteger ); }
bool _bind( qint16& t) { return set(t, qBindExpectedInteger ); }
bool _bind( qint32& t) { return set(t, qBindExpectedInteger ); }
bool _bind( qint64& t) { return set(t, qBindExpectedInteger ); }
bool _bind( quint8& t) { return set(t, qBindExpectedPositiveNumber); }
bool _bind( quint16& t) { return set(t, qBindExpectedPositiveNumber); }
bool _bind( quint32& t) { return set(t, qBindExpectedPositiveNumber); }
bool _bind( quint64& t) { return set(t, qBindExpectedPositiveNumber); }
bool _bind( float& t) { return set(t, qBindExpectedDecimal ); }
bool _bind( double& t) { return set(t, qBindExpectedDecimal ); }
bool _sequence(quint32* =nullptr) { if (levels.last().isGroup) { levels.push(Level( )); return true; } _reportError(qBindExpectedSequence); return false; }
bool _record (quint32* =nullptr) { if (levels.last().isGroup) { levels.push(Level(qBindExpectedItem)); return true; } _reportError(qBindExpectedRecord ); return false; }
bool _null ( ) { if (settings->value(key()).isNull()) { return true; } _reportError(qBindExpectedNull ); return false; }
bool _item(QIdentifier& k) { levels.last().key=k ; return true; }
bool _item( ) { levels.last().idx++ ; return true; }
bool _out ( ) { levels.pop(); settings->endGroup(); return true; }
bool tryBind( qint8& t) { return set(t, qBindExpectedInteger ); }
bool tryBind( qint16& t) { return set(t, qBindExpectedInteger ); }
bool tryBind( qint32& t) { return set(t, qBindExpectedInteger ); }
bool tryBind( qint64& t) { return set(t, qBindExpectedInteger ); }
bool tryBind( quint8& t) { return set(t, qBindExpectedPositiveNumber); }
bool tryBind( quint16& t) { return set(t, qBindExpectedPositiveNumber); }
bool tryBind( quint32& t) { return set(t, qBindExpectedPositiveNumber); }
bool tryBind( quint64& t) { return set(t, qBindExpectedPositiveNumber); }
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 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() { return settings; }
void _setChoice(bool v) { isChoice=v; }
......
......@@ -47,53 +47,53 @@
// //////////////////////////////////////////////////////////////////////////
// QBind<T,QVariant*> support for the fixed set of QVariantBuilder's BindNative types
class QVariantBuilder : public IWriter
class QVariantBuilder : public QAbstractValueWriter
{
Q_DISABLE_COPY(QVariantBuilder)
public:
QVariantBuilder(QVariant* v) : variant(v) { Q_ASSERT(v); }
~QVariantBuilder() { while (!levels.isEmpty()) _out(); }
~QVariantBuilder() { while (!levels.isEmpty()) tryOut(); }
void reset(QVariant* v) { variant=v; Q_ASSERT(v); levels.resize(0); }
// Shortcuts
/**/ Val<Cursor> value ( ) { return Cursor(this).value(); }
/**/ Seq<Cursor> sequence(quint32* s=nullptr) { return Cursor(this).value().sequence ( s); }
template<typename T> Cursor bind ( T&& t) { return Cursor(this).value().bind(std::forward<T>(t)); }
/**/ QValue value ( ) { return QValueStatus(this).value(); }
/**/ QSeq<QValueStatus> 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)); }
protected:
// TODO Support _meta to be able to cache and restitute all metadata as well as data+datatype
bool _bind(QUtf8DataView u) {
return _bind(QUtf8Data(u));
bool tryBind(QUtf8DataView u) {
return tryBind(QUtf8Data(u));
}
bool _bind( QStringView u) { return _bind(u.toString()); }
bool tryBind( QStringView u) { return tryBind(u.toString()); }
bool _bind( QUtf8Data&& t) {
bool tryBind( QUtf8Data&& t) {
set(QVariant::fromValue(t));
return true;
}
bool _bind( QString&& t) { set(QVariant::fromValue(t)); return true; }
bool _bind( bool&& t) { set(QVariant::fromValue(t)); return true; }
bool _bind( qint8&& t) { set(QVariant::fromValue(t)); return true; }
bool _bind( quint8&& t) { set(QVariant::fromValue(t)); return true; }
bool _bind( qint16&& t) { set(QVariant::fromValue(t)); return true; }
bool _bind( quint16&& t) { set(QVariant::fromValue(t)); return true; }
bool _bind( qint32&& t) { set(QVariant::fromValue(t)); return true; }
bool _bind( quint32&& t) { set(QVariant::fromValue(t)); return true; }
bool _bind( qint64&& t) { set(QVariant::fromValue(t)); return true; }
bool _bind( quint64&& t) { set(QVariant::fromValue(t)); return true; }
bool _bind( float&& t) { set(QVariant::fromValue(t)); return true; }
bool _bind( double&& t) { set(QVariant::fromValue(t)); return true; }
bool _bind( QByteArray&& t) { set(QVariant::fromValue(t)); return true; }
bool _bind( QVariant&& v) { set( v ); return true; }
bool _null( ) { set(QVariant ( )); return true; }
bool _sequence(quint32* =nullptr) { levels.push(Level( )); return true; }
bool _record (quint32* =nullptr) { levels.push(Level(qBindExpectedItem)); return true; }
bool _item(QIdentifier& n) { levels.last().key=n ; return true; }
bool _item( ) { levels.last().key=QIdentifier(); return true; }
bool _out ( ) { auto level = levels.pop(); set(!level.key.isNull() ? QVariant(level.object) : QVariant(level.array)); return true; }
bool tryBind( QString&& t) { set(QVariant::fromValue(t)); return true; }
bool tryBind( bool&& t) { set(QVariant::fromValue(t)); return true; }
bool tryBind( qint8&& t) { set(QVariant::fromValue(t)); return true; }
bool tryBind( quint8&& t) { set(QVariant::fromValue(t)); return true; }
bool tryBind( qint16&& t) { set(QVariant::fromValue(t)); return true; }
bool tryBind( quint16&& t) { set(QVariant::fromValue(t)); return true; }
bool tryBind( qint32&& t) { set(QVariant::fromValue(t)); return true; }
bool tryBind( quint32&& t) { set(QVariant::fromValue(t)); return true; }
bool tryBind( qint64&& t) { set(QVariant::fromValue(t)); return true; }
bool tryBind( quint64&& t) { set(QVariant::fromValue(t)); return true; }
bool tryBind( float&& t) { set(QVariant::fromValue(t)); return true; }
bool tryBind( double&& t) { set(QVariant::fromValue(t)); return true; }
bool tryBind( QByteArray&& t) { set(QVariant::fromValue(t)); return true; }
bool tryBind( QVariant&& v) { set( v ); return true; }
bool tryNull( ) { set(QVariant ( )); return true; }
bool trySequence(quint32* =nullptr) { levels.push(Level( )); return true; }
bool tryRecord (quint32* =nullptr) { levels.push(Level(qBindExpectedItem)); return true; }
bool tryItem(QIdentifier& n) { levels.last().key=n ; return true; }
bool tryItem( ) { levels.last().key=QIdentifier(); return true; }
bool tryOut ( ) { auto level = levels.pop(); set(!level.key.isNull() ? QVariant(level.object) : QVariant(level.array)); return true; }
private:
void set(const QVariant& v) {
if (levels.isEmpty()) {
......@@ -114,7 +114,7 @@ private:
// --------------------------------------------------------------------------
class QVariantVisitor : public IReader
class QVariantVisitor : public QAbstractValueReader
{
Q_DISABLE_COPY(QVariantVisitor)
public:
......@@ -122,7 +122,7 @@ public:
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<class T> T bind(Val<T>&& value) { return value.bind(QUtf8Data(error.utf8()+' '+path.utf8())); } };
struct Error { QIdentifierLiteral error; QAsciiData path; template<class T> T bind(QVal<T>&& value) { return value.bind(QUtf8Data(error.utf8()+' '+path.utf8())); } };
QVector<Error> errors;
QAsciiData currentPath() {
......@@ -137,45 +137,45 @@ protected:
// TODO Support _meta to be able to cache and restitute all metadata as well as data+datatype
template<typename T>
bool _bind(T& t) { if (current()->type()==qMetaTypeId<T>()) { t = current()->value<T>(); return true; } _reportError(QIdentifierLiteral("ExpectedDeclaredMetatypeT")); return false; }
bool tryBind(T& t) { if (current()->type()==qMetaTypeId<T>()) { t = current()->value<T>(); return true; } _reportError(QIdentifierLiteral("ExpectedDeclaredMetatypeT")); return false; }
bool _bind( QUtf8Data& t) { if (current()->userType() // TODO ->type() if QUtf8Data lands into Qt
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 _bind( QString& t) { if (current()->type()==qMetaTypeId< QString>()) { t = current()->value< QString>(); return true; } _reportError(qBindExpectedText ); return false; }
bool _bind( bool& t) { if (current()->type()==qMetaTypeId< bool>()) { t = current()->value< bool>(); return true; } _reportError(qBindExpectedBoolean ); return false; }
bool _bind(QByteArray& t) { if (current()->type()==qMetaTypeId<QByteArray>()) { t = current()->value<QByteArray>(); return true; } _reportError(qBindExpectedBytes ); 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<QByteArray>()) { t = current()->value<QByteArray>(); return true; } _reportError(qBindExpectedBytes ); return false; }
// Convert numerical types to strictly larger ones // TODO convert all compatible values
bool _bind( qint8& t) { if (current()->type()==qMetaTypeId< qint8>()) { t = current()->value< qint8>(); return true; } _reportError(qBindExpectedInteger ); return false; }
bool _bind( qint16& t) { if (current()->type()==qMetaTypeId< qint8>()||
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; }
bool _bind( qint32& t) { if (current()->type()==qMetaTypeId< qint8>()||
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; }
bool _bind( qint64& t) { if (current()->type()==qMetaTypeId< qint8>()||
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 _bind( quint8& t) { if (current()->type()==qMetaTypeId< quint8>()) { t = current()->value< quint8>(); return true; } _reportError(qBindExpectedPositiveNumber); return false; }
bool _bind( quint16& t) { if (current()->type()==qMetaTypeId< quint8>()||
bool tryBind( quint8& t) { if (current()->type()==qMetaTypeId< quint8>()) { t = current()->value< quint8>(); return true; } _reportError(qBindExpectedPositiveNumber); return false; }
bool tryBind( quint16& t) { if (current()->type