Commit 4ed1be47 authored by EXT Arnaud Clère's avatar EXT Arnaud Clère
Browse files
parents 3119490e 28e572ac
......@@ -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>;
template<class TResult> friend class QTableWriter;