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

Added QCborReader

Improved and simplified writers not needing specific BindSupport
(contrary to DataWriter)
Replaced QBindTable with optional Val::meta(...) to support Cbor tags,
Xml attributes, and even N-dimensional structures like
QStandardModelItem that support tables, arrays and trees
parent fd1e4905
......@@ -30,6 +30,5 @@ HEADERS += \
QBind_impl.h \
QCbor_impl.h \
QJson_impl.h \
QBindTable_impl.h \
QVariant_impl.h \
QData_impl.h
......@@ -55,17 +55,21 @@ protected: \
#include <QtCore/qbytearray.h>
using Name = const char*;
// //////////////////////////////////////////////////////////////////////////
// Error handling helpers
// Standard error names
static Name qBindExpectedNull = "Expected null" ;
static Name qBindExpectedSequence = "Expected sequence";
static Name qBindExpectedRecord = "Expected record" ;
static Name qBindExpectedText = "Expected text" ;
static Name qBindExpectedBytes = "Expected bytes" ;
static Name qBindExpectedDecimal = "Expected decimal" ;
static Name qBindExpectedBoolean = "Expected boolean" ;
static const char* qBindExpectedNull = "Expected null" ;
static const char* qBindExpectedSequence = "Expected sequence";
static const char* qBindExpectedRecord = "Expected record" ;
static const char* qBindExpectedText = "Expected text" ;
static const char* qBindExpectedBytes = "Expected bytes" ;
static const char* qBindExpectedDecimal = "Expected decimal" ;
static const char* qBindExpectedBoolean = "Expected boolean" ;
static const char* qBindIgnoredCharacter = "Ignored character";
static Name qBindIgnoredCharacter = "Ignored character";
static Name qBindIgnoredBytes = "Ignored bytes" ;
template<class T>
class ScopedChoice //!< for accurate error reporting
......@@ -78,6 +82,33 @@ private:
T& t;
};
// //////////////////////////////////////////////////////////////////////////
// Standard meta data names
#include <map>
#include <QtCore/qstring.h>
using MetaData = std::map<Name,QString>;
static Name qmType = "type";
// N-dimensional data structures that specific IWriter can optimize
// BEWARE though that nested calls to IWriter are not guaranteed to follow the declared structure (this would prevent reusing general QBind functors for nested data structures)
//! Sequence of records projected to columns (item names are always the same in a fixed order)
static Name qmTable = "table";
static Name qmColumns = "columns";// comma-separated names
static Name qmRows = "rows"; // comma-separated names (optional)
//! Sequence of nested non-empty sequences of definite sizes
static Name qmArray = "array";
static Name qmSizes = "sizes"; // comma-separated natural numbers
//! Sequence of records where item(qmNodes) contains nested trees
static Name qmTrees = "trees";
static Name qmNodes = "nodes";
// //////////////////////////////////////////////////////////////////////////
// QBind<TResult,T>
......@@ -131,9 +162,6 @@ template<class TImpl> struct BindSupport<TImpl,void> : BindGeneric {};
template<class T_> class Rec; //!< a Record data structure defined below
template<class T_> class Seq; //!< a Sequence data structure defined below
template<class TResult> class QBindTable;
using Names = QVector<QByteArray>;
template<class T_> class Val //!< a choice of sequence(), record(), null(), or any value with a textual representation
{
Q_DISABLE_COPY(Val)
......@@ -147,17 +175,15 @@ public:
/**/ operator bool() { return m_out.operator bool(); } //!< to drive QBind<TResult,T>() traversal
TImpl* operator ->() { return m_out.operator ->(); }
// TODO? add (const char* type) variants for sequence and record to let Writer Impl choose specialised repr (unordered dict..., XML tag)
template<typename T> T_ table (T&& t, const Names& cols); // TODO return a Val<Tab<T_>> instead of taking a T&&?
template<typename T> T_ table (T&& t ); // Binding default-constructed T::value_type to get cols
/**/ Val<T_> meta ( MetaData&& m) { auto mref=m; return meta(mref); }
/**/ Val<T_> meta ( MetaData& m) { if (Q_LIKELY(m_out)) m_out->_meta(m); return std::move(*this); }
// TODO ? Add bind with default T specialised for Reader/Writer (do not write default?) or use meta since default T values may not be appropriate (e.g. for bool)
/**/ Seq<T_> sequence(quint32* rows=nullptr) { if (Q_LIKELY(m_out) && m_out->_sequence (rows)) return Seq<T_>(std::move(m_out)); else return Seq<T_>(); }
/**/ Rec<T_> record (quint32* cols=nullptr) { if (Q_LIKELY(m_out) && m_out->_record (cols)) return Rec<T_>(std::move(m_out)); else return Rec<T_>(); }
/**/ T_ null ( ) { if (Q_LIKELY(m_out) && m_out->_null ( )) return std::move(m_out) ; else return T_ (); }
template<typename T> T_ bind ( T&& t) { if (_bind(BindSupport<TImpl, T>(),std::forward<T> (t))) return std::move(m_out) ; else return T_ (); }
/**/ T_ any ( ) { if (_bind(BindSupport<TImpl,void>() )) return std::move(m_out) ; else return T_ (); }
// TODO Add bind with default T specialised for Reader/Writer (do not write default?)
/**/ Seq<T_> sequence(quint32* s=nullptr) { if (Q_LIKELY(m_out) && m_out->_sequence ( s)) return Seq<T_>(std::move(m_out)); else return Seq<T_>(); }
/**/ Rec<T_> record (quint32* s=nullptr) { if (Q_LIKELY(m_out) && m_out->_record ( s)) return Rec<T_>(std::move(m_out)); else return Rec<T_>(); }
/**/ T_ null ( ) { if (Q_LIKELY(m_out) && m_out->_null ( )) return std::move(m_out) ; else return T_ (); }
template<typename T> T_ bind ( T&& t) { if (_bind(BindSupport<TImpl, T>(),std::forward<T>(t))) return std::move(m_out) ; else return T_ (); }
/**/ T_ any ( ) { if (_bind(BindSupport<TImpl,void>() )) return std::move(m_out) ; else return T_ (); }
T_ bind(const char* const& t); // special handling of both (const char*)'s const& and &&
private:
......@@ -192,11 +218,13 @@ public:
TResult result() { return operator TResult(); }
// Shortcuts
/**/ Seq<Seq<T_>> sequence () { return item().sequence (); }
/**/ Rec<Seq<T_>> record () { return item().record (); }
/**/ Seq<T_> null () { return item().null (); }
template<typename T> Seq<T_> bind(T&& t) { return item().bind(std::forward<T>(t)); }
/**/ Seq<T_> any () { return item().any (); }
/**/ Val<Seq<T_>> meta ( MetaData&& m) { return item().meta ( m); }
/**/ Val<Seq<T_>> meta ( MetaData& m) { return item().meta ( m); }
/**/ Seq<Seq<T_>> sequence(quint32* s=nullptr) { return item().sequence ( s); }
/**/ Rec<Seq<T_>> record (quint32* s=nullptr) { return item().record ( s); }
/**/ Seq<T_> null ( ) { return item().null ( ); }
template<typename T> Seq<T_> bind ( T&& t) { return item().bind(std::forward<T>(t)); }
/**/ Seq<T_> any ( ) { return item().any ( ); }
private:
template<class TSrcResult, typename TDst, typename TEnabledIf> friend struct QBind; // restricting to <TSrcResult, Val<TDst>&&, IsReader<TSrcResult>>; is not possible in C++11
Val<TResult> _unsafeItem() { if (Q_LIKELY(m_out) && m_out->_item()) return Val<TResult>(m_out._unsafeCopy()); else return Val<TResult>(); }
......@@ -223,19 +251,21 @@ public:
/**/ operator bool() { return m_out.operator bool(); } //!< to drive QBind<TResult,T>() traversal
TImpl* operator ->() { return m_out.operator ->(); }
Val<Rec<T_>> item(const char* key) { QByteArray k(key); // TODO use QByteArray::fromRawData(key,int(strlen(key)) for Writers
if (Q_LIKELY(m_out) && m_out->_item(k )) return Val<Rec<T_>>(std::move(*this)); else return Val<Rec<T_>>(); }
Val<Rec<T_>> item(QByteArray& key) { if (Q_LIKELY(m_out) && m_out->_item(key)) return Val<Rec<T_>>(std::move(*this)); else return Val<Rec<T_>>(); }
/**/ T_ out ( ) { if (Q_LIKELY(m_out) && m_out->_out ( )) return std::move(m_out) ; else return T_ (); }
Val<Rec<T_>> item( Name name) { QByteArray n(name); // TODO use QByteArray::fromRawData(key,int(strlen(key)) for Writers
if (Q_LIKELY(m_out) && m_out->_item(n )) return Val<Rec<T_>>(std::move(*this)); else return Val<Rec<T_>>(); }
Val<Rec<T_>> item(QByteArray& name) { if (Q_LIKELY(m_out) && m_out->_item(name)) return Val<Rec<T_>>(std::move(*this)); else return Val<Rec<T_>>(); }
/**/ T_ out ( ) { if (Q_LIKELY(m_out) && m_out->_out ( )) return std::move(m_out) ; else return T_ (); }
operator TResult() { return out(); /* calls T_::operator TResult() if T_ != TResult */ }
TResult result() { return operator TResult(); }
// Shortcuts
/**/ Seq<Rec<T_>> sequence(const char* key ) { return item(key).sequence (); }
/**/ Rec<Rec<T_>> record (const char* key ) { return item(key).record (); }
/**/ Rec<T_> null (const char* key ) { return item(key).null (); }
template<typename T> Rec<T_> bind (const char* key, T&& t) { return item(key).bind(std::forward<T>(t)); }
/**/ Val<Rec<T_>> meta (Name n, MetaData&& m) { return item(n).meta ( m); }
/**/ Val<Rec<T_>> meta (Name n, MetaData& m) { return item(n).meta ( m); }
/**/ Seq<Rec<T_>> sequence(Name n, quint32* s=nullptr) { return item(n).sequence ( s); }
/**/ Rec<Rec<T_>> record (Name n, quint32* s=nullptr) { return item(n).record ( s); }
/**/ Rec<T_> null (Name n ) { return item(n).null ( ); }
template<typename T> Rec<T_> bind (Name n, T&& t) { return item(n).bind(std::forward<T>(t)); }
private:
template<class TSrcResult, typename TDst, typename TEnabledIf> friend struct QBind; // restricting to <TSrcResult, Val<TDst>&&, IsReader<TSrcResult>>; is not possible in C++11
Val<TResult> _unsafeItem(QByteArray& key) { if (Q_LIKELY(m_out) && m_out->_item(key)) return Val<TResult>(m_out._unsafeCopy()); else return Val<TResult>(); }
......@@ -272,10 +302,12 @@ public:
Val<TResult> value() { return Val<TResult>(std::move(*static_cast<TResult*>(this))); }
// Shortcuts
/**/ Seq<TResult> sequence () { return value().sequence (); }
/**/ Rec<TResult> record () { return value().record (); }
/**/ TResult null () { return value().null (); }
template<typename T> TResult bind(T&& t) { return value().bind(std::forward<T>(t)); }
/**/ Val<TResult> meta ( MetaData&& m) { return value().meta ( m); }
/**/ Val<TResult> meta ( MetaData& m) { return value().meta ( m); }
/**/ Seq<TResult> sequence(quint32* s=nullptr) { return value().sequence ( s); }
/**/ Rec<TResult> record (quint32* s=nullptr) { return value().record ( s); }
/**/ TResult null ( ) { return value().null ( ); }
template<typename T> TResult bind ( T&& t) { return value().bind(std::forward<T>(t)); }
void reportError(const char* error) { if (m) m->reportError(error); }
protected:
......@@ -311,17 +343,18 @@ public:
Val<TResult> value() { return Val<TResult>(std::move(*static_cast<TResult*>(this))); }
// Shortcuts
/**/ Seq<TResult> sequence () { return value().sequence (); }
/**/ Rec<TResult> record () { return value().record (); }
/**/ TResult null () { return value().null (); }
template<typename T> TResult bind(T&& t) { return value().bind(std::forward<T>(t)); }
/**/ Val<TResult> meta ( MetaData&& m) { return value().meta ( m); }
/**/ Val<TResult> meta ( MetaData& m) { return value().meta ( m); }
/**/ Seq<TResult> sequence(quint32* s=nullptr) { return value().sequence ( s); }
/**/ Rec<TResult> record (quint32* s=nullptr) { return value().record ( s); }
/**/ TResult null ( ) { return value().null ( ); }
template<typename T> TResult bind ( T&& t) { return value().bind(std::forward<T>(t)); }
void reportError(const char*) {} // a TResult with BindMode::Write will not encounter bind mismatches but only write errors independent from the current state
protected:
friend class ScopedChoice<Val<TResult>>;
void setChoice(bool) {} // not implemented like reportError
private:
friend class IWriter;
friend struct IWriter;
template<typename TResult> friend class QTableWriter;
template<typename T_> friend class Val;
template<typename T_> friend class Seq;
......@@ -599,15 +632,23 @@ struct IWriter
{
virtual ~IWriter() = default;
//! Write status for current value
virtual operator bool() { return true; }
virtual bool _sequence(quint32* cols=nullptr) = 0;
virtual bool _record (quint32* rows=nullptr) = 0;
//! Meta data is data about current data (e.g. tag, attribute, style, etc.)
//! \warning It is ignored by default and subject to various interpretation, so users should meta data standards or adopt existing ones like XSD for sake of interoperability
virtual void _meta ( MetaData& ) {}
virtual bool _sequence(quint32* size=nullptr) = 0;
virtual bool _record (quint32* size=nullptr) = 0;
virtual bool _null ( ) = 0;
virtual bool _item ( QByteArray& k) = 0;
virtual bool _item ( ) = 0;
virtual bool _out ( ) { return true; }
//! End of sequence or record
//! Few IWriter need to process this (contiguous IWriter need to mark the end of indefinite sequences and records for instance)
virtual bool _out() { return true; }
virtual bool _bind ( const char* s) = 0;
......@@ -637,6 +678,8 @@ protected:
friend class QMovedWriter<Writer>;
template<class T_> friend class Val; // enables calling methods below
void _meta ( MetaData& m) { Q_ASSERT(impl); impl->_meta ( m); }
bool _sequence(quint32* cols=nullptr) { Q_ASSERT(impl); return impl->_sequence(cols); }
bool _record (quint32* rows=nullptr) { Q_ASSERT(impl); return impl->_record (rows); }
bool _null ( ) { Q_ASSERT(impl); return impl->_null ( ); }
......
......@@ -100,22 +100,26 @@ enum {
// //////////////////////////////////////////////////////////////////////////
// QBind<QCborWriter,T> support
class CborWriter : public IWriter, public QMovedWriter<CborWriter>
class CborWriter : public IWriter
{
protected:
CborWriter(const CborWriter &o) : QMovedWriter(), io(o.io) {} // It is important that scoped copies start again from level 0 to avoid duplicate _out() calls
CborWriter &operator=(const CborWriter &) = delete;
Q_DISABLE_COPY(CborWriter)
public:
Q_ENABLE_MOVE(CborWriter, std::swap(io, o.io); std::swap(levels, o.levels);)
~CborWriter() { while (!levels.empty()) _out(); }
CborWriter(QIODevice* io) : io(io) { Q_ASSERT(io); }
operator bool() { return io; } // for QMovedWriter
protected:
friend class QMovedWriter<CborWriter>;
template<class TResult> friend class QTableWriter;
template<class T_> friend class Val; // calls methods below
// Shortcuts
/**/ Val<Writer> value ( ) { return write().value ( ); }
/**/ Val<Writer> meta ( MetaData&& m) { return write().meta ( m); }
/**/ Val<Writer> meta ( MetaData& m) { return write().meta ( m); }
/**/ Seq<Writer> sequence(quint32* s=nullptr) { return write().sequence ( s); }
/**/ Rec<Writer> record (quint32* s=nullptr) { return write().record ( s); }
/**/ Writer null ( ) { return write().null ( ); }
template<typename T> Writer bind ( T&& t) { return write().bind(std::forward<T>(t)); }
private:
inline Writer write() { return Writer(this); }
protected:
bool _sequence(quint32* rows=nullptr) { levels.push_back(rows);
return Q_LIKELY (rows)
? putInteger (*rows , cbor::ArrayType )
......@@ -149,9 +153,6 @@ protected:
bool _bind(qulonglong& v) { return _bind(v, nullptr); }
bool _bind( qlonglong& v) { return _bind(v, nullptr); }
template<class T_> friend class Seq; // calls methods below
template<class T_> friend class Rec; // calls methods below
bool _item(QByteArray& k) { return (Q_LIKELY(putInteger(quint64(k.size()), cbor::TextStringType)) && (Q_LIKELY(io->write(k)) || k.isEmpty())); }
bool _item( ) { return true ; }
bool _out ( ) { bool definite = levels.back(); levels.pop_back(); return definite ? true : putSimpleValue(cbor::Break); }
......@@ -194,6 +195,168 @@ template<typename T> struct BindSupport<CborWriter,T&,typename std::enable_if<st
//template bool CborWriter::_bind<qulonglong&>(qulonglong& v);
//template bool CborWriter::_bind< qlonglong&>( qlonglong& v);
// --------------------------------------------------------------------------
#include <QtCore/qcborstream.h>
#include <QtCore/qstack.h>
class QCborReaderImpl;
class QCborReader : public QScopedResult<QCborReader, QCborReaderImpl, BindMode::Read>
{
Q_PROTECTED_COPY(QCborReader)
public:
Q_ENABLE_MOVE_DEFAULT(QCborReader)
QCborReader(QCborStreamReader* io);
private:
friend class QScopedResult<QCborReader, QCborReaderImpl, BindMode::Read>;
friend class QCborReaderImpl; // uses method below
QCborReader(QCborReaderImpl* outer) : QScopedResult(outer, false) {}
};
class QCborReaderImpl
{
Q_DISABLE_COPY(QCborReaderImpl)
QCborStreamReader* io;
bool isChoice = false;
public:
QCborReaderImpl(QCborStreamReader* io) : io(io) { Q_ASSERT(io); }
struct Error { const char* error; qint64 index; template<class T> T bind(Val<T> value) { QByteArray ba(error); ba.append(' ').append(QByteArray::number(index)); return value.bind(ba.constData()); } };
QVector<Error> errors;
protected:
friend class ScopedChoice<Val<QCborReader>>;
void setChoice(bool v) { isChoice=v; }
friend class QScopedResult<QCborReader, QCborReaderImpl, BindMode::Read>;
void reportError(const char* error) { if (!isChoice) errors.append(Error{ error, io->currentOffset() }); }
template<class T_> friend class Val; // calls methods below
bool _sequence(quint32* =nullptr) { if (io->isArray ()) { io->enterContainer(); return true; } reportError(qBindExpectedSequence); return false; }
bool _record (quint32* =nullptr) { if (io->isMap ()) { io->enterContainer(); return true; } reportError(qBindExpectedRecord ); return false; }
bool _null ( ) { if (io->isNull ()) { return true; } reportError(qBindExpectedNull ); return false; }
bool _bind ( QString& s) { if (io->isString()) {
s.clear();
auto r = io->readString();
while (r.status == QCborStreamReader::Ok) {
s.append(r.data);
r = io->readString();
}
if (r.status == QCborStreamReader::Error) {
if (!s.isEmpty()) {
reportError(qBindIgnoredBytes);
s.clear();
}
return false;
}
return true;
} else { reportError(qBindExpectedBytes); return false; } }
bool _bind ( QByteArray& s) { if (io->isByteArray()) {
s.clear();
auto r = io->readByteArray();
while (r.status == QCborStreamReader::Ok) {
s.append(r.data);
r = io->readByteArray();
}
if (r.status == QCborStreamReader::Error) {
if (!s.isEmpty()) {
reportError(qBindIgnoredBytes);
s.clear();
}
return false;
}
return true;
} else { reportError(qBindExpectedBytes); return false; } }
bool _bind ( bool& b) { if (io->isBool()) {
b=io->toBool();
return true;
} else { reportError(qBindExpectedBoolean); return false; } }
bool _bind ( float& n) { double d; qlonglong i; bool b; auto r=getNumber(d,i,b); if (r) n=float(d); return r; }
bool _bind ( double& n) { double d; qlonglong i; bool b; auto r=getNumber(d,i,b); if (r) n= d ; return r; }
// This dispatch would be more simple with C++17 constexpr if
template<typename T> bool _bind(T& t, typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value>::type* =nullptr) {
double d; qlonglong i; bool isNegative;
auto r=getNumber(d, i, isNegative); if (r && !isNegative) t=i; return r; }
template<typename T> bool _bind(T& t, typename std::enable_if<std::is_integral<T>::value && std:: is_signed<T>::value>::type* =nullptr) {
double d; qlonglong i; bool isNegative;
auto r=getNumber(d, i, isNegative); if (r ) t=i; return r; }
bool _bind ( ) { // TODO use io->next() ?
isChoice=true; QString s; QByteArray t; double d; bool b;
((_sequence() && _out())
||_null()
||_bind(b)
||_bind(d)
||_bind(t)
||_bind(s)
);
isChoice=false;
return true;
}
template<class T_> friend class Seq; // calls methods below
template<class T_> friend class Rec; // calls methods below
// TODO cache key:value pairs coming out of order wrt to T items() calls
bool _item(QByteArray& k) { if (!io->hasNext()) {
return false;
}
QString s;
if (!_bind(s)) {
return false;
}
k = s.toUtf8();
return true; }
bool _item( ) { if (!io->hasNext()) {
return false;
}
return true; }
bool _out ( ) { while (io->hasNext()) {
_bind();
}
return io->leaveContainer(); }
private:
bool getNumber(double& d, qlonglong&i, bool& isNegative) {
if (io->isFloat()) {
d = double(io->toFloat());
i = qlonglong(d);
return true;
}
if (io->isDouble()) {
d = io->toDouble();
i = qlonglong(d);
return true;
}
if (io->isFloat16()) {
d = double(io->toFloat16());
i = qlonglong(d);
return true;
}
if (io->isNegativeInteger()) {
i = - io->toInteger(); // TODO toNegativeInteger to get full range ?
isNegative = true;
return true;
}
if (io->isInteger()) {
i = io->toInteger();
isNegative = false;
return true;
}
reportError(qBindExpectedDecimal); return false;
}
};
QCborReader::QCborReader(QCborStreamReader* io) : QScopedResult(new QCborReaderImpl(io), true) {}
template<> struct BindSupport<QCborReaderImpl, void> : BindNative {};
template<> struct BindSupport<QCborReaderImpl, QString&> : BindNative {};
template<> struct BindSupport<QCborReaderImpl,QByteArray&> : BindNative {};
template<> struct BindSupport<QCborReaderImpl, bool&> : BindNative {};
template<> struct BindSupport<QCborReaderImpl, float&> : BindNative {};
template<> struct BindSupport<QCborReaderImpl, double&> : BindNative {};
template<typename T> struct BindSupport<QCborReaderImpl,T&,typename std::enable_if<std::is_integral<T>::value>::type> : BindNative {};
#if QT_VERSION>=QT_VERSION_CHECK(5,12,0)
// //////////////////////////////////////////////////////////////////////////
// QBind<QCborValue,T> support
......
......@@ -54,7 +54,7 @@
// - use the same set of registered QMetaTypeIds with operator<< defined
// - use compatible ByteOrder, FloatingPointPrecision, and Version
class DataWriter : public QMovedWriter<DataWriter>
class DataWriter : public IWriter, public QMovedWriter<DataWriter> // allows using QDataStream << for small numeric types
{
protected:
DataWriter(const DataWriter &o) : QMovedWriter<DataWriter>(), io(o.io) {}
......
......@@ -72,7 +72,6 @@ class QJsonBuilderImpl
public:
QJsonBuilderImpl(QJsonValue* v) : value(v) { Q_ASSERT(v); }
protected:
friend class ScopedChoice<Val<QJsonBuilder>>;
void setChoice(bool) {} // not implemented like reportError
friend class QScopedResult<QJsonBuilder, QJsonBuilderImpl, BindMode::Write>; // for slight optimization of fluent interface shortcuts
......@@ -156,7 +155,6 @@ public:
return path;
}
protected:
friend class ScopedChoice<Val<QJsonVisitor>>;
void setChoice(bool v) { isChoice=v; }
friend class QScopedResult<QJsonVisitor, JsonVisitorImpl, BindMode::Read>;
......@@ -195,33 +193,24 @@ template<typename T> struct BindSupport<JsonVisitorImpl,T&,typename std::enable_
// --------------------------------------------------------------------------
class QJsonWriterImpl;
class QJsonWriter : public QScopedResult<QJsonWriter, QJsonWriterImpl, BindMode::Write>
class QJsonWriter : public IWriter
{
Q_PROTECTED_COPY(QJsonWriter)
Q_DISABLE_COPY(QJsonWriter)
public:
Q_ENABLE_MOVE_DEFAULT(QJsonWriter)
QJsonWriter(QIODevice* io);
QJsonWriter(QIODevice* io) : io(io) { Q_ASSERT(io); }
~QJsonWriter() { for (auto&& level : levels) io->write(level.end); }
// Shortcuts
/**/ Val<Writer> value ( ) { return write().value ( ); }
/**/ Val<Writer> meta ( MetaData&& m) { return write().meta ( m); }
/**/ Val<Writer> meta ( MetaData& m) { return write().meta ( m); }
/**/ Seq<Writer> sequence(quint32* s=nullptr) { return write().sequence ( s); }
/**/ Rec<Writer> record (quint32* s=nullptr) { return write().record ( s); }
/**/ Writer null ( ) { return write().null ( ); }
template<typename T> Writer bind ( T&& t) { return write().bind(std::forward<T>(t)); }
private:
friend class QScopedResult<QJsonWriter, QJsonWriterImpl, BindMode::Write>;
friend class QJsonWriterImpl; // uses method below
QJsonWriter(QJsonWriterImpl* outer) : QScopedResult(outer, false) {}
};
class QJsonWriterImpl : public IWriter
{
Q_DISABLE_COPY(QJsonWriterImpl)
public:
QJsonWriterImpl(QIODevice* io) : io(io) { Q_ASSERT(io); }
~QJsonWriterImpl() { for (auto&& level : levels) io->write(level.end); }
inline Writer write() { return Writer(this); }
protected:
friend class ScopedChoice<Val<QJsonWriter>>;
void setChoice(bool) { /* not implemented like reportError */ }
friend class QScopedResult<QJsonWriter, QJsonWriterImpl, BindMode::Write>;
void reportError(const char*) { /* a TResult with BindMode::Write will not encounter bind mismatches but only write errors independent from the current state */ }
template<class T_> friend class Val; // calls methods below
bool _sequence(quint32* rows=nullptr) { Q_UNUSED(rows); levels.push(Step{"","]"}); return io->write("["); }
bool _record (quint32* cols=nullptr) { Q_UNUSED(cols); levels.push(Step{"","}"}); return io->write("{"); }
bool _null ( ) { return io->write( "null"); }
......@@ -239,9 +228,6 @@ protected:
bool _bind(qulonglong& v) { return _bind(v, nullptr); }
bool _bind( qlonglong& v) { return _bind(v, nullptr); }
template<class T_> friend class Seq; // calls methods below
template<class T_> friend class Rec; // calls methods below
bool _item(QByteArray& k) { auto r=(io->write(levels.last().sep) || *levels.last().sep=='\0') && putString(k) && io->write(":"); levels.last().sep = ","; return r; }
bool _item( ) { auto r=(io->write(levels.last().sep) || *levels.last().sep=='\0') ; levels.last().sep = ","; return r; }
bool _out ( ) { return io->write(levels.pop() .end); }
......@@ -279,12 +265,6 @@ private:
QIODevice* io;
QStack<Step> levels = QStack<Step>(); // TODO Replace with 2 std::vector<bool> for performance //!< minimal dynamic context to implement out() and ensure well-formedness in case TResult is abandoned
};
QJsonWriter::QJsonWriter(QIODevice* io) : QScopedResult(new QJsonWriterImpl(io), true) {}
template<> struct BindSupport<QJsonWriterImpl,const char*> : BindNative {};
template<> struct BindSupport<QJsonWriterImpl, bool&> : BindNative {};
template<> struct BindSupport<QJsonWriterImpl, float&> : BindNative {};
template<> struct BindSupport<QJsonWriterImpl, double&> : BindNative {};
template<typename T> struct BindSupport<QJsonWriterImpl,T&,typename std::enable_if<std::is_integral<T>::value>::type> : BindNative {};
// --------------------------------------------------------------------------
......
......@@ -47,39 +47,41 @@
// //////////////////////////////////////////////////////////////////////////
// QBind<QVariant*,T> support for the fixed set of QVariantBuilder's BindNative types
class QVariantBuilder : public QMovedWriter<QVariantBuilder>
class QVariantBuilder : public IWriter
{
protected:
QVariantBuilder(const QVariantBuilder &o) : QMovedWriter(), value(o.value), levels() {} // It is important that scoped copies start again from level 0 to avoid duplicate _out() calls
QVariantBuilder &operator=(const QVariantBuilder &) = delete;
Q_DISABLE_COPY(QVariantBuilder)
public:
Q_ENABLE_MOVE(QVariantBuilder, std::swap(value, o.value); std::swap(levels, o.levels); )
QVariantBuilder(QVariant* v) : value(v) { Q_ASSERT(v); }
QVariantBuilder(QVariant* v) : variant(v) { Q_ASSERT(v); }
~QVariantBuilder() { while (!levels.isEmpty()) _out(); }
operator bool() { return true; } // for QMovedWriter
protected:
friend class QMovedWriter<QVariantBuilder>;