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
This diff is collapsed.
......@@ -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" );
......
......@@ -42,10 +42,10 @@
#include <QtCore/qvariant.h>
#include <QtCore/qstack.h>
#include "QBind.h"
#include "QValue.h"
// //////////////////////////////////////////////////////////////////////////
// QBind<T,QVariant*> support for the fixed set of QVariantBuilder's BindNative types
// QVariant* support for the fixed set of QVariantBuilder's BindNative types
class QVariantBuilder : public QAbstractValueWriter
{
......
......@@ -43,10 +43,10 @@
#include <QtCore/qxmlstream.h>
#include "QBind.h"
#include "QValue.h"
// //////////////////////////////////////////////////////////////////////////
// QBind<T,QXmlStreamWriter> support
// QXml* support
//! \warning Since XML has no standard way of encoding data structures, we choose a rather implicit one
//! based on the fact that XML elements are implicit data structures unless they contain text without elements,
......
This diff is collapsed.
......@@ -2,7 +2,7 @@
#include <QtCore/qmetaobject.h>
#include "QBind.h"
#include "QValue.h"
class Phone {
Q_GADGET
......
......@@ -38,18 +38,18 @@
* **
* ****************************************************************************/
// The core of QBind proof-of-concept including:
// - a fluent interface for describing logical data structures, providing auto completion and well-formedness guarantees
// - a set of recursive QBind<T> functors binding QAbstractValue and T according to their logical data structure
// The core of QTransmogrifier proof-of-concept including:
// - a QValue fluent interface for describing logical data structures, providing auto completion and well-formedness guarantees
// - a set of recursive QTransmogrifier<T> functors binding QAbstractValue and T according to their logical data structure
#include "QBind.h"
#include "QValue.h"
// Extensible set of QAbstractValue implementations providing concrete syntax to the logical data structure in a specific Mode among Write, Read, ...
#include "QJson_impl.h" // QJsonWriter, QJsonReader, QJsonBuilder, QJsonVisitor and QBind<QJsonValue,_> support
#include "QJson_impl.h" // QJsonWriter, QJsonReader, QJsonBuilder, QJsonVisitor and QTransmogrifier<QJsonValue,_> support
#include "QCbor_impl.h" // QCborWriter demonstrating the performance potential of the approach
#include "QData_impl.h" // QDataReader, QDataWriter demonstrating a QDataStream with implicit types for benchmarking
#include "QVariant_impl.h" // QVariantBuilder, QVariantVisitor and QBind<QVariant,_> support
#include "QVariant_impl.h" // QVariantBuilder, QVariantVisitor and QTransmogrifier<QVariant,_> support
#include "QModel_impl.h" // Q*ModelWriter support
#include "QXml_impl.h" // QXmlWriter support
#include "QSettings_impl.h" // QSettings* support
......@@ -72,7 +72,7 @@
#include <tuple>
// //////////////////////////////////////////////////////////////////////////
// QBind-enabled types examples
// QValue-enabled types examples
// Using Qt's meta-object system
......@@ -92,7 +92,7 @@ struct Person
r = s.out();
r = r.bind("height" ,height );
r = r.bind("age" ,age ,-1); // reads null() as -1
r = r.bind("phones" ,phones ); // recursive calls to QBind will take care of that part
r = r.bind("phones" ,phones ); // recursive calls to QTransmogrifier will take care of that part
r = r.bind("comments",comments);
r = r.bind("children",children);
return r; // automagically closes opened record()
......@@ -102,7 +102,7 @@ struct Person
// For comparison purposes:
#include<QtCore/qdebug.h>
QDebug &operator<<(QDebug &out, const Person &p)
QDebug &operator<<(QDebug &out, const Person &p)
{
return out.nospace() << "Person(" // items count and names are implicit
<< p.firstName << ", " << p.lastName << ", " // sequence is implicit
......@@ -244,13 +244,13 @@ private:
};
// //////////////////////////////////////////////////////////////////////////
// QBind<T> examples using external bind methods
// QTransmogrifier<T> examples using external bind methods
#include <QtGui/qcolor.h>
//! QBind<QColor,_> with special support for QDataStream compatibility using meta()
//! QTransmogrifier<QColor> with special support for QDataStream compatibility using meta()
template<>
struct QBind<QColor> {
struct QTransmogrifier<QColor> {
static QValueStatus bind(QValue&& v, QColor&& c) {
QColor copy(c);
return bind(std::move(v),copy);
......@@ -1065,8 +1065,8 @@ int main(int argc, char *argv[])
{
QFileInfo iniFile(QDir::currentPath()+"/../QBind");
QSettings::setPath(QSettings::IniFormat,QSettings::UserScope,iniFile.path());
QSettings ini(QSettings::IniFormat,QSettings::UserScope,"QBind","sample");
QSettings nat( QSettings::UserScope,"QBind","sample");
QSettings ini(QSettings::IniFormat,QSettings::UserScope,"QTransmogrifier","sample");
QSettings nat( QSettings::UserScope,"QTransmogrifier","sample");
QVector<QSettingsReader::Error> errors;
//---------------------------------------------------------------------
START {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment