Commit bd379ac7 authored by EXT Arnaud Clère's avatar EXT Arnaud Clère

Renamed to QTransmogrifier

parent 9e809e33
# Design
The core QBind implementation (excluding QAbstractValue implementations) is a few hundreds line of C++11 using templates defined
The core QTransmogrifier 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
QBind is more general than (de)serialization and should be understood as a generic way to traverse[^1] a C++ dataset and
QTransmogrifier is more general than (de)serialization and should be understood as a generic way to traverse[^1] a C++ dataset and
another generic dataset, binding the related parts together. In effect:
* the traversal may be partial, leaving out unrelated dataset parts (satisfying R2)
* the same traversal may be used to:
......@@ -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 QValueMode (Read,Write,...) to determine whether to read the generic dataset or write it according to the C++ one.
This traversal is driven by QTransmogrifier<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
......@@ -24,7 +24,7 @@ QDebug and QDataStream translate all data to a "flat" sequence of characters/byt
be determined for sure by reading the code that produced it. But R1, R2, R3 require that we describe our data in a little bit more
detail. Moreover, W1 and RW2 require that we choose a careful compromise between data format features.
QBind allows binding C++ `data` to a choice of:
QTransmogrifier allows binding C++ `data` to a choice of:
* `sequence` of adjacent `data` `item`s
* `record` of named `data` `item`s
* `null` value (meaning no information available on `data`)
......@@ -34,16 +34,16 @@ QBind allows binding C++ `data` to a choice of:
- numbers (integral/floating-point, unsigned/signed, 8/16/32/64 bits)
- *date/time (TBD)*
- *uuid (TBD)*
* Generically supported T values for which a specialized QBind<T>::bind() is defined
* Generically supported T values for which a specialized QTransmogrifier<T>::bind() is defined
We argue that QBind allows lossless conversions between all supported formats, and that the addition of optional metadata (RW3)
We argue that QTransmogrifier allows lossless conversions between all supported formats, and that the addition of optional metadata (RW3)
can address most peculiarities of the supported formats. However, it may not always conveniently address formats that do not have standard
ways of representing data structures such as XML (e.g. binding the Person type with enough metadata to conform the result to
[xCard schema](https://tools.ietf.org/html/rfc6351) would be cumbersome).
## QBind grammar
## QTransmogrifier grammar
The QBind traversal is formally described by the following recursive automaton:
The QTransmogrifier traversal is formally described by the following recursive automaton:
```mermaid
graph LR
subgraph QVal
......@@ -74,22 +74,22 @@ The automaton is implemented as follows:
- 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`
- `bind<T>()` calls are forwarded to the actual `QAbstractValue` or generic `QBind` depending on `BindSupport<T>`:
- `bind<T>()` calls are forwarded to the actual `QAbstractValue` or generic `QTransmogrifier` depending on `BindSupport<T>`:
- BindNative : **QAbstractValue** interface method
- BindGeneric : **QBind** template specialization for T
- 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)
## 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 QValueStatus.
QTransmogrifier 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,
A default QTransmogrifier specialization attempts to call `T::bind(...)` to conveniently bind `T* this` without having to understand template syntax,
rvalue or forwarding references. Such custom implementations are facilitated by the fluent interface below.
## Convenient fluent interface
A convenient side-effect of encoding the QBind traversal in the type system is that smart C++ editors offer QBind-aware code completion
A convenient side-effect of encoding the QTransmogrifier traversal in the type system is that smart C++ editors offer QTransmogrifier-aware code completion
to the fluent interface making it similar to using an advanced XML editor for typing XML tags. Say, after typing `Value(myImpl).` the
editor will propose to either `bind(myData.item)`, or to construct a `sequence()`, `record()` or `null()` value.
......@@ -120,7 +120,7 @@ optimized and replaced with just the following operations:
[^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
`QTransmogrifier<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 QAbstractValue*
......@@ -141,7 +141,7 @@ Other than that, write performance depends on several factors:
## Read robustness
Performance is not so important for Read. But compared to manually calling non-virtual, format-specific implementations, QBind
Performance is not so important for Read. But compared to manually calling non-virtual, format-specific implementations, QTransmogrifier
enforces well-formedness checks necessary to reliably read data coming from unknown sources (QAbstractValue implementations being responsible
for low-level checks).
......@@ -160,6 +160,6 @@ native types simplifying again the implementations (see TextWriter example).
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,QAbstractValue> but QBind<QColor>
for each TImpl. For instance, a QTransmogrifier<QColor,QDataStream> may be implemented differently from QTransmogrifier<QColor,QAbstractValue> but QTransmogrifier<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.
......@@ -45,17 +45,17 @@ gcc:QMAKE_CXXFLAGS += -ftemplate-backtrace-limit=0
#QMAKE_EXTRA_COMPILERS += protoc
SOURCES += \
QBind.cpp \
QData.cpp \
QValue.cpp \
data.cpp \
main.cpp
HEADERS += \
QBind.h \
QCbor_impl.h \
QData.h \
QJson_impl.h \
QSettings_impl.h \
QValue.h \
QVariant_impl.h \
QModel_impl.h \
QData_impl.h \
......
......@@ -44,7 +44,7 @@
#include <QtCore/qiodevice.h>
#include <QtCore/qendian.h>
#include "QBind.h"
#include "QValue.h"
namespace cbor {
......@@ -98,7 +98,7 @@ enum {
} // cbor
// //////////////////////////////////////////////////////////////////////////
// QBind<T,QCbor*> support
// QTransmogrifier<T,QCbor*> support
class QCborWriter : public QAbstractValueWriter // TODO Support CBOR tags for QAbstractValue BindNative types and multi-dimensional meta() (e.g. support qmColumns with http://cbor.schmorp.de/stringref, qmSizes with https://datatracker.ietf.org/doc/draft-ietf-cbor-array-tags/?include_text=1 )
{
......@@ -422,11 +422,11 @@ protected:
bool tryOut ( ) { if (caching) { return caching->tryOut() && cacheOut(); }
levels.pop();
while (hasNext()) {
_any();
tryAny();
}
return leaveContainer(); }
bool _any() { return next(); }
bool tryAny() { return next(); }
bool _isOk() const noexcept { return const_cast<QCborReader*>(this)->lastError()==QCborError::NoError; }
void _setChoice(bool b) { isChoice = b; }
void _reportError(QIdentifierLiteral e) { if (!isChoice) errors.append(Error{ e, currentOffset(), lastError() }); }
......@@ -467,14 +467,14 @@ private:
};
// //////////////////////////////////////////////////////////////////////////
// QBind<QCbor*,_> support
// QTransmogrifier<QCbor*,_> support
#include <QtCore/qjsonvalue.h>
#include <QtCore/qjsonarray.h>
#include <QtCore/qjsonobject.h>
template<>
struct QBind<QCborValue> {
struct QTransmogrifier<QCborValue> {
static QValueStatus bind(QValue&& v, QCborValue&& j) {
if (v->mode()==Write) {
if (j.isMap ()) return v.bind(j.toMap ());
......@@ -514,7 +514,7 @@ struct QBind<QCborValue> {
};
template<>
struct QBind<QCborArray> {
struct QTransmogrifier<QCborArray> {
static QValueStatus bind(QValue&& v, QCborArray&& j) {
if (v->mode()==Write) {
quint32 size=quint32(j.size());
......@@ -545,7 +545,7 @@ struct QBind<QCborArray> {
};
template<>
struct QBind<QCborMap> {
struct QTransmogrifier<QCborMap> {
static QValueStatus bind(QValue&& v, QCborMap&& j) {
if (v->mode()==Write) {
quint32 size=quint32(j.size());
......
......@@ -44,16 +44,16 @@
#include <QtCore/qdatastream.h>
#include <QtCore/qvariant.h>
#include "QBind.h"
#include "QValue.h"
// //////////////////////////////////////////////////////////////////////////
// QBind<T,QDataStream*> support
// QTransmogrifier<T,QDataStream*> support
// 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 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()
class QDataWriter : public QAbstractValueWriter // allows runtime flexibility but requires QTransmogrifier<T> in addition to T::operator<<(QDataStream&) and does not warrant QDataStream operators compatibility if QTransmogrifier<T> ignores qmDataStreamVersion in meta()
{
Q_DISABLE_COPY(QDataWriter)
public:
......@@ -108,4 +108,4 @@ private:
// \remark Providing a general QDataReader may be deceiving because the QDataStream structure is implicit, thus
// a reader is usually assumed to statically know the data type to read based on the stream origin and version.
// Though QVariant can be used for parts which type are not statically known though, such as the dynamic type of a message coming from a socket.
// Finally, QDataStream version can be retrieved from meta() to adapt to data schema changes over the time \see QBind<QColor> for an example
// Finally, QDataStream version can be retrieved from meta() to adapt to data schema changes over the time \see QTransmogrifier<QColor> for an example
......@@ -47,10 +47,10 @@
#include <QtCore/qiodevice.h>
#include <QtCore/qstack.h>
#include "QBind.h"
#include "QValue.h"
// //////////////////////////////////////////////////////////////////////////
// QBind<T,QJson*> support
// QTransmogrifier<T,QJson*> support
class QJsonBuilder : public QAbstractValueWriter
{
......@@ -198,20 +198,20 @@ private:
else if (utf8[i] == '"') { putChar('\\'); putChar( '"'); }
else if (utf8[i] < 0|| // UTF-8 sequence bytes
utf8[i] >= ' ') { putChar(utf8[i]); }
else {
putChar('\\'); switch (utf8[i]) {
case '\t' : putChar( 't'); break;
case '\b' : putChar( 'b'); break;
case '\f' : putChar( 'f'); break;
case '\n' : putChar( 'n'); break;
case '\r' : putChar( 'r'); break;
default : putChar( 'u'); {
char high=((utf8[i]) / 16), low=((utf8[i]) % 16);
putChar( '0' );
putChar( '0' );
putChar(high+(high<10?'0':'A'-10));
putChar( low+( low<10?'0':'A'-10)); } break;
}
else {
putChar('\\'); switch (utf8[i]) {
case '\t' : putChar( 't'); break;
case '\b' : putChar( 'b'); break;
case '\f' : putChar( 'f'); break;
case '\n' : putChar( 'n'); break;
case '\r' : putChar( 'r'); break;
default : putChar( 'u'); {
char high=((utf8[i]) / 16), low=((utf8[i]) % 16);
putChar( '0' );
putChar( '0' );
putChar(high+(high<10?'0':'A'-10));
putChar( low+( low<10?'0':'A'-10)); } break;
}
}
}
return putChar('"');
......@@ -249,66 +249,66 @@ public:
QVector<Error> errors;
// Shortcuts
/**/ QValue value ( ) { return QValueStatus(this).value(); }
/**/ QSeq<QValueStatus> sequence(quint32* s=nullptr) { return QValueStatus(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> QValueStatus bind ( T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
protected:
enum CachedNumber : quint8 { None=0, Integer, FloatingPoint };
bool trySequence(quint32* s=nullptr) { if (caching) { cacheLevel++; return caching->trySequence(s); }
if (get('[', "[{\"ntf-0123456789.")) { levels.push(Step(-1,"]")); return true; } _reportError(qBindExpectedSequence); return false; }
if (get('[', "[{\"ntf-0123456789.")) { levels.push(Step(-1,"]")); return true; } _reportError(qBindExpectedSequence); return false; }
bool tryRecord (quint32* s=nullptr) { if (caching) { cacheLevel++; return caching->tryRecord(s); }
if (get('{', "[{\"ntf-0123456789.")) { levels.push(Step(-1,"}")); return true; } _reportError(qBindExpectedRecord ); return false; }
if (get('{', "[{\"ntf-0123456789.")) { levels.push(Step(-1,"}")); return true; } _reportError(qBindExpectedRecord ); return false; }
bool tryNull ( ) { if (caching) { cacheLevel++; return caching->tryNull() && cacheOut(); }
if (get('n', "[{\"ntf-0123456789.") &&
get('u', "[{\"" ) &&
get('l', "[{\"" ) &&
get('l', "[{\"" )) {
return true;
} else { _reportError(qBindExpectedNull); return false; } }
if (get('n', "[{\"ntf-0123456789.") &&
get('u', "[{\"" ) &&
get('l', "[{\"" ) &&
get('l', "[{\"" )) {
return true;
} else { _reportError(qBindExpectedNull); return false; } }
bool tryBind ( QUtf8Data& s) { if (caching) { cacheLevel++; return caching->tryBind(s) && cacheOut(); }
QByteArray u;
if ( get('"', "[{\"ntf-0123456789.")) { u.resize(0); char c;
while ((c=getCharInString()) != '\0' ) { u.append(c); }
s=QUtf8Data(u);
return get('"');
} else { _reportError(qBindExpectedText); return false; } }
QByteArray u;
if ( get('"', "[{\"ntf-0123456789.")) { u.resize(0); char c;
while ((c=getCharInString()) != '\0' ) { u.append(c); }
s=QUtf8Data(u);
return get('"');
} else { _reportError(qBindExpectedText); return false; } }
bool tryBind ( QString& s) { QUtf8Data u; if (tryBind(u)) { s = QString(u.utf8()); return true; } return false; }
bool tryBind ( bool& b) { if (caching) { cacheLevel++; return caching->tryBind(bool(b)) && cacheOut(); }
if (get('t', "[{\"ntf-0123456789.") &&
get('r', "[{\"" ) &&
get('u', "[{\"" ) &&
get('e', "[{\"" )) { b=true;
return true;
} else
if (get('f', "[{\"ntf-0123456789.") &&
get('a', "[{\"" ) &&
get('l', "[{\"" ) &&
get('s', "[{\"" ) &&
get('e', "[{\"" )) { b=false;
return true;
} else { _reportError(qBindExpectedBoolean); return false; } }
if (get('t', "[{\"ntf-0123456789.") &&
get('r', "[{\"" ) &&
get('u', "[{\"" ) &&
get('e', "[{\"" )) { b=true;
return true;
} else
if (get('f', "[{\"ntf-0123456789.") &&
get('a', "[{\"" ) &&
get('l', "[{\"" ) &&
get('s', "[{\"" ) &&
get('e', "[{\"" )) { b=false;
return true;
} else { _reportError(qBindExpectedBoolean); return false; } }
bool tryBind ( float& n) { if (caching) { cacheLevel++; return caching->tryBind(double(n)) && cacheOut(); }
if (getNumber()==None) return false; // already reported qBindExpectedDecimal
if (d<double( std::numeric_limits<float >::min())||
double( std::numeric_limits<float >::max())<d) { _reportError(qBindExpectedSmallerNumber ); return false; }
n = float(d); cachedNumber=None; return true; }
if (getNumber()==None) return false; // already reported qBindExpectedDecimal
if (d<double( std::numeric_limits<float >::min())||
double( std::numeric_limits<float >::max())<d) { _reportError(qBindExpectedSmallerNumber ); return false; }
n = float(d); cachedNumber=None; return true; }
bool tryBind ( double& n) { if (caching) { cacheLevel++; return caching->tryBind(double(n)) && cacheOut(); }
if (getNumber()==None) return false; // already reported qBindExpectedDecimal
n = d ; cachedNumber=None; return true; }
if (getNumber()==None) return false; // already reported qBindExpectedDecimal
n = d ; cachedNumber=None; return true; }
bool tryBind ( quint64& t) { if (caching) { cacheLevel++; return caching->tryBind(double(t)) && cacheOut(); }
auto r=getNumber();
if (r==None) return false; // already reported qBindExpectedDecimal
if (r==FloatingPoint) { _reportError(qBindExpectedInteger); return false; }
if (neg) { _reportError(qBindExpectedPositiveInteger); return false; }
t = i ; cachedNumber=None; return true; }
auto r=getNumber();
if (r==None) return false; // already reported qBindExpectedDecimal
if (r==FloatingPoint) { _reportError(qBindExpectedInteger); return false; }
if (neg) { _reportError(qBindExpectedPositiveInteger); return false; }
t = i ; cachedNumber=None; return true; }
bool tryBind ( qint64& t) { if (caching) { cacheLevel++; return caching->tryBind(double(t)) && cacheOut(); }
auto r=getNumber();
if (r==None) return false; // already reported qBindExpectedDecimal
if (r==FloatingPoint) { _reportError(qBindExpectedInteger); return false; }
if (!neg && i<quint64( std::numeric_limits<qint64>::max())) { t = qint64(i); cachedNumber=None; return true; }
if ( neg && i<quint64(-std::numeric_limits<qint64>::min())) { t = -qint64(i); cachedNumber=None; return true; }
_reportError(qBindExpectedSmallerNumber ); return false; }
auto r=getNumber();
if (r==None) return false; // already reported qBindExpectedDecimal
if (r==FloatingPoint) { _reportError(qBindExpectedInteger); return false; }
if (!neg && i<quint64( std::numeric_limits<qint64>::max())) { t = qint64(i); cachedNumber=None; return true; }
if ( neg && i<quint64(-std::numeric_limits<qint64>::min())) { t = -qint64(i); cachedNumber=None; return true; }
_reportError(qBindExpectedSmallerNumber ); return false; }
bool tryBind ( QVariant& dst) {
_setChoice(true);
......@@ -330,54 +330,54 @@ protected:
}
bool tryOut ( ) { if (caching) { return caching->tryOut() && cacheOut(); }
auto level = levels.pop();
while (get(',', level.end)) {
_any();
}
return get(*level.end, "}"); }
auto level = levels.pop();
while (get(',', level.end)) {
tryAny();
}
return get(*level.end, "}"); }
bool tryItem(QIdentifierLiteral u) { if (caching) { return caching->tryItem(u); }
QIdentifier s;
while (true) {
if (levels.last().cachedItems. contains(QIdentifier(u))) { // must be checked before we consume tryItem(s)
cachedValue = levels.last().cachedItems.take(QIdentifier(u));
cacheReader.reset(&cachedValue);
Q_ASSERT(!cacheLevel);
caching = &cacheReader; // let outer QValueStatus drive QJsonReader depending on bound T
return true;
}
else if (!tryItem(s)) { // record() end reached
return false;
}
else if (u == s ) {
return true ;
}
else { // read cachedValue using a dedicated QValueStatus and QBind<QValue> accepting value of any 'shape' (outer QBind 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;
levels.last().cachedItems.insert(s, cachedValue);
continue; // until !tryItem(s) or u==s
}
}}
QIdentifier s;
while (true) {
if (levels.last().cachedItems. contains(QIdentifier(u))) { // must be checked before we consume tryItem(s)
cachedValue = levels.last().cachedItems.take(QIdentifier(u));
cacheReader.reset(&cachedValue);
Q_ASSERT(!cacheLevel);
caching = &cacheReader; // let outer QValueStatus drive QJsonReader depending on bound T
return true;
}
else if (!tryItem(s)) { // record() end reached
return false;
}
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)
cachedValue = QJsonValue();
cacheWriter.reset(&cachedValue);
if (!QValueStatus(this).value().bind(QValueStatus(&cacheWriter).value())) return false;
levels.last().cachedItems.insert(s, cachedValue);
continue; // until !tryItem(s) or u==s
}
}}
bool tryItem(QIdentifier& k) { if (caching) { return caching->tryItem(k); }
Step& level = levels.last();
if ((-1 < level.index && !get(',',level.end)) || next("[{\"ntf-0123456789.",'}')=='}') {
return false;
}
level.index++;
QUtf8Data s;
if (!tryBind(s)) {
return false;
}
k = QIdentifier(s.utf8()); // TODO check errors
return get(':',level.end); }
Step& level = levels.last();
if ((-1 < level.index && !get(',',level.end)) || next("[{\"ntf-0123456789.",'}')=='}') {
return false;
}
level.index++;
QUtf8Data s;
if (!tryBind(s)) {
return false;
}
k = QIdentifier(s.utf8()); // TODO check errors
return get(':',level.end); }
bool tryItem( ) { if (caching) { return caching->tryItem(); }
Step& level = levels.last();
if ((-1 < level.index && !get(',',level.end)) || next("[{\"ntf-0123456789.",']')==']') {
return false;
}
level.index++;
return true; }
Step& level = levels.last();
if ((-1 < level.index && !get(',',level.end)) || next("[{\"ntf-0123456789.",']')==']') {
return false;
}
level.index++;
return true; }
bool _isOk() const noexcept { return io; }
void _setChoice(bool v) { isChoice=v; }
......@@ -513,14 +513,14 @@ private:
};
// //////////////////////////////////////////////////////////////////////////
// QBind<QJson*,_> support
// QTransmogrifier<QJson*,_> support
#include <QtCore/qjsonvalue.h>
#include <QtCore/qjsonarray.h>
#include <QtCore/qjsonobject.h>
template<>
struct QBind<QJsonValue> {
struct QTransmogrifier<QJsonValue> {
static QValueStatus bind(QValue&& v, QJsonValue&& j) {
if (v->mode()==Write) {
if (j.isObject()) return v.bind(j.toObject());
......@@ -556,7 +556,7 @@ struct QBind<QJsonValue> {
};
template<>
struct QBind<QJsonArray> {
struct QTransmogrifier<QJsonArray> {
static QValueStatus bind(QValue&& v, QJsonArray&& j) {
if (v->mode()==Write) {
quint32 size=quint32(j.size());
......@@ -587,7 +587,7 @@ struct QBind<QJsonArray> {
};
template<>
struct QBind<QJsonObject> {
struct QTransmogrifier<QJsonObject> {
static QValueStatus bind(QValue&& v, QJsonObject&& j) {
if (v->mode()==Write) {
quint32 size=quint32(j.size());
......
......@@ -47,11 +47,11 @@
#include <QtCore/qbuffer.h>
#include <QtGui/qcolor.h>
#include "QBind.h"
#include "QValue.h"
#include "QJson_impl.h"
// //////////////////////////////////////////////////////////////////////////
// QBind<T,Q*Model> support
// QTransmogrifier<T,Q*Model> support
class QModelBind : public QAbstractValue
{
......
......@@ -43,10 +43,10 @@
#include <QtCore/qvariant.h>
#include <QtCore/qstack.h>
#include "QBind.h"
#include "QValue.h"
// //////////////////////////////////////////////////////////////////////////
// QBind<T,QVariant*> support for the fixed set of QSettingsWriter's BindNative types
// QSettings* support for the fixed set of QSettingsWriter's BindNative types
class QSettingsWriter : public QAbstractValueWriter
{
......@@ -55,8 +55,8 @@ public:
QSettingsWriter(QSettings* s) : settings(s) { Q_ASSERT(s); levels.push(Level(qBindExpectedItem)); }
// Shortcuts
/**/ QValue value ( ) { return QValueStatus(this).value(); }
/**/ QSeq<QValueStatus> sequence(quint32* s=nullptr) { return QValueStatus(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> QValueStatus bind ( T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
protected:
bool tryBind ( quint8&& t) { settings->setValue(key(), int(t) ); return true; }
......
#include "QBind.h"
#include "QValue.h"
QIdentifierLiteral qBindExpectedItem ("ExpectedItem" );
QIdentifierLiteral qBindExpectedNull ("ExpectedNull" );
......
......@@ -87,7 +87,7 @@ extern QIdentifierLiteral qBindIgnoredBytes ;
extern QIdentifierLiteral qmDataStreamVersion; //!< Allows QAbstractValue support of QDataStream
// N-dimensional data structures for which specific QAbstractValueWriter may have optimized implementations
// BEWARE though that nested calls to QAbstractValueWriter are not guaranteed to follow the declared structure (this would prevent reusing general QBind functors for nested data structures)
// BEWARE though that nested calls to QAbstractValueWriter are not guaranteed to follow the declared structure (this would prevent reusing general QTransmogrifier functors for nested data structures)
//! Sequence of records can be implemented as table with columns below (record item names are always the same in a fixed order)
//! Contains comma-separated names
......@@ -107,13 +107,13 @@ extern QIdentifierLiteral qmName;
extern QIdentifierLiteral qmColor;
// //////////////////////////////////////////////////////////////////////////
// QBind<T>
// QTransmogrifier<T>
enum QValueMode { Invalid=0, Read=1, Write=2 }; //!< Specifies QBind::bind traversal and processing (the design would support other QValueMode like Append or Diff)
enum QValueMode { Invalid=0, Read=1, Write=2 }; //!< Specifies QTransmogrifier::bind traversal and processing (the design would support other QValueMode like Append or Diff)
struct BindGeneric {};
struct BindNative {};
//!< Specifies whether QVal calls QAbstractValue::tryBind or QBind<T>::bind
//!< Specifies whether QVal calls QAbstractValue::tryBind or QTransmogrifier<T>::bind
//!< \remark It would be possible to remove BindSupport by defining all QAbstractValue overloads in QVal
template<typename T, typename TEnabledIf=void> struct BindSupport : BindGeneric {};
......@@ -148,7 +148,7 @@ template<> struct BindSupport< QVariant> : BindNative {};
struct QAbstractValue {
virtual ~QAbstractValue() = default;
virtual QValueMode mode() const noexcept = 0; //!< \remark a static constexpr QValueMode Mode did not exhibit noticeable performance improvements and may trigger twice more code generation for Read/Write independant QBind like Person::bind
virtual QValueMode mode() const noexcept = 0; //!< \remark a static constexpr QValueMode Mode did not exhibit noticeable performance improvements and may trigger twice more code generation for Read/Write independant QTransmogrifier like Person::bind
virtual bool _isOk() const noexcept = 0; //!< Current operation status
......@@ -219,7 +219,7 @@ struct QAbstractValue {
virtual bool tryBind( QByteArray&& r) = 0;
virtual bool tryBind( QVariant&& r) = 0;
virtual bool _any() { return tryNull(); }
virtual bool tryAny() { return tryNull(); }
//! \warning meta() is ignored by default and subject to various interpretation, so users should define or adopt existing meta data standards like XSD for sake of interoperability
virtual void _meta(QIdentifierLiteral&, QAsciiData&) {}
......@@ -238,10 +238,10 @@ template<class T> using RemoveCvRef = typename std::remove_cv<typename std::remo
//! A QValueStatus is 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 reportError().
//! Its protected interface is used by QVal, QSeq, QRec to only allow a new operation QAbstractValue operation from the previous valid QValueStatus.
//! Its public interface only allows checking the status of resulting QAbstractValue operation(s) using operator bool() and to reportError().
//! Its protected interface is used by QVal, QSeq, QRec to only allow new QAbstractValue operation from the previous valid QValueStatus.
//!
//! It allows to easily add QBind 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 QValueStatus(this).value()
//! \see TextWriter in main.cpp
//!
//! \remark QValueStatus allows using the fluent interface without checking intermediate status by setting impl=nullptr on error
......@@ -257,10 +257,10 @@ public: