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

Added QBind for c arrays, QList, QMap, QColor

Made Cbor performance close to the minimum with well-formedness
guarantees using putBigEndian similar to QDataStream and
std::vector<bool> (no malloc until deep levels)
Made QWritable costless with std::function
Made QWritable flexible with QWritable<Writer> + IWriter
Added IWriter for even simpler implementations
WIP: Added QVariant and table experiments
parent c9e64c4a
......@@ -4,8 +4,6 @@
#
#-------------------------------------------------
QT -= gui
TARGET = QBind
CONFIG += console
CONFIG -= app_bundle
......@@ -23,7 +21,7 @@ DEFINES += QT_DEPRECATED_WARNINGS
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
QMAKE_CXXFLAGS += -ftemplate-backtrace-limit=0
gcc:QMAKE_CXXFLAGS += -ftemplate-backtrace-limit=0
SOURCES += \
main.cpp
......@@ -32,5 +30,6 @@ HEADERS += \
QBind_impl.h \
QCbor_impl.h \
QJson_impl.h \
QDataStream_impl.h \
QBindTable_impl.h
QBindTable_impl.h \
QVariant_impl.h \
QData_impl.h
......@@ -43,8 +43,10 @@
#include <QtCore/qvector.h>
#include <QtCore/qmap.h>
//#include <QtCore/qcbormap.h>
#include "QBind_impl.h"
#include "QJson_impl.h"
// //////////////////////////////////////////////////////////////////////////
// QBind<QTableWriter,T> support
......@@ -53,6 +55,9 @@
/// record()
/// .bind("cols",names,QVector<const char*>())
/// .bind("rows",sequence() of sequence(names.size()).bind(v1)...)
///
/// \warning This kind of mapping is really specific to selected TResult (TSV, CBOR data-tables...)
/// whereas other TResult would bind T to native
template<class TResult>
class QTableWriter : public QMovedWriter<QTableWriter<TResult>>
{
......@@ -60,8 +65,8 @@ class QTableWriter : public QMovedWriter<QTableWriter<TResult>>
Names names; //!< of bound columns
quint32 max=0; //!< bound columns (we need to take its address)
enum : int {
T=0, //!< table() called and nothing else
// H=1, //!< outer record() called
T=0, //!< nothing called
// H=1, //!< TODO ? outer record() called
S=1, //!< sequence() called, expecting out() or item()... calls
R=2, //!< record () called, expecting out() or item(key)... calls
};
......@@ -70,10 +75,10 @@ class QTableWriter : public QMovedWriter<QTableWriter<TResult>>
int bound=-1; // bound record item
QMap<int,QWritable<TResult>> cache; // for record() items bound too soon w.r.t. names
protected:
QTableWriter(const QTableWriter &o) : QMovedWriter<QTableWriter<TResult>>(), r(o.r), names(o.names), max(o.max), d(o.d), col(o.col), bound(o.bound), cache(o.cache) {} // It is important that scoped copies start again from level 0 to avoid duplicate _out() calls
QTableWriter(const QTableWriter &o) : QMovedWriter<QTableWriter<TResult>>(), r(o.r), names(o.names), max(o.max), d(o.d), col(o.col), bound(o.bound), cache(o.cache) {}
QTableWriter &operator=(const QTableWriter &) = delete;
public:
Q_ENABLE_MOVE(QTableWriter, std::swap(r, o.r); names=o.names; max=o.max; d=o.d; col=o.col; bound=o.bound; cache=o.cache; ) // state other than r is copied to facilitate debugging
Q_ENABLE_MOVE(QTableWriter, std::swap(r, o.r); std::swap(names,o.names); max=o.max; d=o.d; col=o.col; bound=o.bound; std::swap(cache,o.cache); ) // (d,col,bound) copied to facilitate debugging
QTableWriter(TResult* r, const Names& c) : r(r), names(c), max(quint32(names.size())) {
// if (r->_record()) {
// ++d;
......@@ -169,11 +174,83 @@ protected:
template<class TResult,typename T> struct BindSupport<QTableWriter<TResult>,T> : BindSupport<TResult,T> {};
template<class TResult_>
template<typename T>
TResult_ Val<TResult_>::table(const Names& cols, T&& t) {
template<typename Ts>
TResult_ Val<TResult_>::table(Ts&& ts, const Names& cols) {
Q_ASSERT(cols.size()>0);
auto unsafeCopy(m_out._unsafeCopy());
return QTableWriter<TResult_>(&unsafeCopy,cols).bind(t);
return QTableWriter<TResult_>(&unsafeCopy,cols).bind(ts);
}
template<class TResult_>
template<typename Ts>
TResult_ Val<TResult_>::table(Ts&& ts) {
using T = typename std::remove_reference<Ts>::type::value_type;
T t; QJsonValue record;
QJsonBuilder(&record).bind(t); // TODO QCborBuilder to avoid char* > QString > toUtf8 conversions
Names cols;
Q_FOREACH(auto&& k, record.toObject().keys()) {
cols.append(k.toUtf8());
}
return table(ts,cols);
}
// TODO template<class TImpl> class QTableReader
// //////////////////////////////////////////////////////////////////////////
// QBind<_,QTable<TSequence>> support allowing BindNative support for, say, TSV, databases, etc.
/// Binds generically any iterable TSequence ts with default-constructible TSequence::Item as:
/// sequence().bind(*ts++).bind(*ts++)...
///
/// \warning Requires binding to a dynamic map to associate record fields to table columns without reflection
/// Achieving good performance would require to define
///
template<typename Ts>
class QTable {
// TODO use QBind<_,T> to map T fields to names?
public:
QTable(Ts&& ts, const Names& cols={}) : m_ts(ts), m_cols(cols) {}
template<class TResult, typename TEnabled = void> TResult bind(Val<TResult> value);
//private:
Ts& m_ts;
Names m_cols;
};
//template<typename Ts>
//template<class TResult, IsWriter<TResult>>
//TResult QTable<Ts>::bind(Val<TResult> value) {
// auto s = value.sequence();
// for (auto&& t : m_ts)
// s = s.bind(t);
// return s;
//}
// PRB
//template<class TResult, class Ts> // where Ts is a Container https://en.cppreference.com/w/cpp/named_req/Container
//struct QBind<TResult, QTable<Ts>&, IsWriter<TResult>> { static TResult bind(Val<TResult> dst, QTable<Ts>& src) {
// using T = typename Ts::item_value;
// T defaultT;
// QCborMap defaultItem;
// // TODO remove unused cols
// auto s(dst.record().bind("cols",defaultItem).sequence("rows"));
// for (auto&& t : src.m_ts) {
// s = s.bind(t); // no added value compared to QVector !
// }
// return s;
//}};
template<class TResult, typename T> // where T is a Container and T::value_type is DefaultConstructible https://en.cppreference.com/w/cpp/types/is_default_constructible
struct QBind<TResult, QTable<T>&, IsReader<TResult>> { static TResult bind(Val<TResult> src, QTable<T>& dst) {
auto s(src.sequence());
Val<Seq<TResult>> i;
while ((i = s.item())) {
T t;
if((s = i.bind(t))) // real value would be to translate from an optimized generic format (sequence() of values without keys) to the record() in QBind<_,T>
dst.insert(t); // PRB: How to map to T
}
return s;
}};
/// Returns QTable<T>(t, cols) deducing T from t
template<typename T> QTable<T> table(T&& t, const Names& cols={})
{
return QTable<T>(t, cols);
};
This diff is collapsed.
......@@ -40,9 +40,9 @@
#pragma once
#include <type_traits>
#include <QtCore/qiodevice.h>
#include <QtCore/qendian.h>
#include <QtCore/qstack.h>
#include "QBind_impl.h"
......@@ -51,51 +51,48 @@ namespace cbor {
//! CBOR Major types
//! Encoded in the high 3 bits of the descriptor byte
//! \see http://tools.ietf.org/html/rfc7049#section-2.1
enum MajorTypes : char {
UnsignedIntegerType = 0,
NegativeIntegerType = 1,
TextStringType = 3,
ArrayType = 4,
MapType = 5,
SimpleTypesType = 7
enum MajorType : char {
UnsignedIntType = 0,
NegativeIntType = 1,
ByteStringType = 2,
TextStringType = 3,
ArrayType = 4,
MapType = 5,
TagType = 6,
SimpleValueType = 7
};
//! CBOR simple and floating point types
//! Encoded in the low 5 bits of the descriptor byte when the Major Type is 7.
enum SimpleTypes : char {
FalseValue = 20,
TrueValue = 21,
NullValue = 22,
SinglePrecisionFloat = 26,
DoublePrecisionFloat = 27,
Break = 31
};
enum : char {
SmallValueBitLength = 5U,
Value8Bit = 24U,
Value16Bit = 25U,
Value32Bit = 26U,
Value64Bit = 27U,
IndefiniteLength = 31U,
MajorTypeShift = SmallValueBitLength,
enum SimpleValue : char {
// Unassigned = 0-19
False = 20,
True = 21,
Null = 22,
// Undefined = 23,
// NextByteValue = 24,
// Next16BitFloat = 25,
Next32BitFloat = 26,
Next64BitFloat = 27,
// Unassigned = 28-30
Break = 31
};
enum : char {
BreakByte = Break | char(SimpleTypesType << MajorTypeShift),
enum MajorValue : char {
// SimpleUnsignedInt= 0-23
NextByteUnsignedInt= 24,
Next16BitUnsignedInt= 25,
Next32BitUnsignedInt= 26,
Next64BitUnsignedInt= 27,
IndefiniteLengthMapByte = IndefiniteLength | char(MapType << MajorTypeShift),
IndefiniteLengthArrayByte = IndefiniteLength | char(ArrayType << MajorTypeShift),
NullByte = NullValue | char(SimpleTypesType << MajorTypeShift),
TrueByte = TrueValue | char(SimpleTypesType << MajorTypeShift),
FalseByte = FalseValue | char(SimpleTypesType << MajorTypeShift)
IndefiniteLength = 31, // along with ArrayType or MapType
};
enum {
Tag = 192,
DateTime = 0,
CborDocument = 55799,
MimeMessage = 36,
};
} // cbor
......@@ -103,15 +100,15 @@ enum {
// //////////////////////////////////////////////////////////////////////////
// QBind<QCborWriter,T> support
class CborWriter : public QMovedWriter<CborWriter>
class CborWriter : public IWriter, public QMovedWriter<CborWriter>
{
protected:
CborWriter(const CborWriter &o) : QMovedWriter(), io(o.io), levels() {} // It is important that scoped copies start again from level 0 to avoid duplicate _out() calls
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;
public:
Q_ENABLE_MOVE(CborWriter, std::swap(io, o.io); std::swap(levels, o.levels); )
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); }
~CborWriter() { while (!levels.isEmpty()) _out(); }
operator bool() { return io; } // for QMovedWriter
protected:
......@@ -119,56 +116,90 @@ protected:
template<class TResult> friend class QTableWriter;
template<class T_> friend class Val; // calls methods below
bool _sequence(quint32* rows=nullptr) { levels.push(rows);
if (rows) return putInteger(cbor:: ArrayType, *rows );
return io->putChar(cbor::IndefiniteLengthArrayByte ); }
bool _record (quint32* cols=nullptr) { levels.push(cols);
if (cols) return putInteger(cbor:: MapType, *cols );
return io->putChar(cbor::IndefiniteLengthMapByte ); }
bool _null ( ) { return io->putChar(cbor::NullByte ); }
bool _bind ( const char*& s) { return putInteger(cbor::TextStringType, strlen(s)) && (io->write(s) || *s=='\0'); }
bool _bind ( bool& b) { return io->putChar(b ? cbor::TrueByte : cbor::FalseByte); }
bool _bind ( float& n) { union { float value; quint32 bits; } number = { n };
char bytes[sizeof(number.bits)]; qToBigEndian(number.bits, bytes);
return (io->putChar(char(cbor::SimpleTypesType << cbor::MajorTypeShift) | cbor::SinglePrecisionFloat)
&& io->write(bytes, sizeof(bytes))); }
bool _bind ( double& n) { union { double value; quint64 bits; } number = { n };
char bytes[sizeof(number.bits)]; qToBigEndian(number.bits, bytes);
return (io->putChar(char(cbor::SimpleTypesType << cbor::MajorTypeShift) | cbor::DoublePrecisionFloat)
&& io->write(bytes, sizeof(bytes))); }
bool _sequence(quint32* rows=nullptr) { levels.push_back(rows);
return Q_LIKELY (rows)
? putInteger (*rows , cbor::ArrayType )
: putMajorValue (cbor::IndefiniteLength, cbor::ArrayType ); }
bool _record (quint32* cols=nullptr) { levels.push_back(cols);
return Q_LIKELY (cols)
? putInteger (*cols , cbor::MapType )
: putMajorValue (cbor::IndefiniteLength, cbor::MapType ); }
bool _null ( ) { return putSimpleValue(cbor::Null ); }
bool _bind ( QByteArray& s) { return putInteger (quint64(s.size()) ,cbor::ByteStringType) && (Q_LIKELY(io->write(s)) || *s=='\0'); }
bool _bind ( const char* s) { return putInteger (strlen(s) ,cbor::TextStringType) && (Q_LIKELY(io->write(s)) || *s=='\0'); }
bool _bind ( QString& s) { static const char mimeHeader[] = "Content-Type:text/plain;charset=utf-16\xD\xA\xD\xA";
QByteArray utf16(QByteArray::fromRawData(reinterpret_cast<const char*>(s.utf16()), s.size()*int(sizeof(QChar))));
return putMajorValue (cbor::NextByteUnsignedInt,cbor::TagType ) && io->putChar(cbor::MimeMessage) &&
putInteger (sizeof(mimeHeader)+
utf16.size() ,cbor::ByteStringType) && io->write(mimeHeader, sizeof(mimeHeader))
&& (Q_LIKELY(io->write(utf16)) || s[0]==QChar('\0')); }
bool _bind ( bool& b) { return putSimpleValue(b ? cbor::True : cbor::False); }
bool _bind ( float& n) { return Q_LIKELY(putSimpleValue(cbor::Next32BitFloat)) && putBigEndian(n); }
bool _bind ( double& n) { return Q_LIKELY(putSimpleValue(cbor::Next64BitFloat)) && putBigEndian(n); }
// 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) {
return putInteger(cbor::UnsignedIntegerType, quint64( t)); }
template<typename T> bool _bind(T t, typename std::enable_if<std::is_integral<T>::value && std:: is_signed<T>::value>::type* =nullptr) {
return t<0 ? putInteger(cbor::NegativeIntegerType, quint64(-1-t)) // see https://tools.ietf.org/html/rfc7049#section-2.1
: putInteger(cbor::UnsignedIntegerType, quint64( t)); }
template<typename T> bool
_bind ( T& t, typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value>::type* =nullptr)
{ return putInteger(quint64( t), cbor::UnsignedIntType); }
template<typename T> bool
_bind ( T& t, typename std::enable_if<std::is_integral<T>::value && std:: is_signed<T>::value>::type* =nullptr)
{ return t<0 ? putInteger(quint64(-1-t), cbor::NegativeIntType) // see https://tools.ietf.org/html/rfc7049#section-2.1
: putInteger(quint64( t), cbor::UnsignedIntType); }
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 (putInteger(cbor::TextStringType, quint64(k.size())) && (io->write(k) || k.isEmpty())); }
bool _item( ) { return true ; }
bool _out ( ) { return levels.pop() ? true : io->putChar (cbor::BreakByte); }
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); }
private:
bool putInteger(char majorType, quint64 n)
inline bool putMajorValue (cbor:: MajorValue v, cbor::MajorType majorType) { return io->putChar (v|char ( majorType << 5)); }
inline bool putSimpleValue(cbor::SimpleValue v ) { return io->putChar (v|char (cbor::SimpleValueType << 5)); }
inline bool putInteger ( quint64 n, cbor::MajorType majorType)
{
// See https://tools.ietf.org/html/rfc7049#section-2.2 if more performance is needed
if (n < 24) { return io->putChar(char(majorType << cbor::MajorTypeShift) | char(n));
} else {
char bytes[8];
if (n <= 0xff ) { qToBigEndian(quint8 (n),bytes); return io->putChar(char(majorType << cbor::MajorTypeShift) | cbor::Value8Bit ) && io->write(bytes,sizeof(quint8 )); }
if (n <= 0xffff ) { qToBigEndian(quint16(n),bytes); return io->putChar(char(majorType << cbor::MajorTypeShift) | cbor::Value16Bit) && io->write(bytes,sizeof(quint16)); }
if (n <= 0xffffffffL) { qToBigEndian(quint32(n),bytes); return io->putChar(char(majorType << cbor::MajorTypeShift) | cbor::Value32Bit) && io->write(bytes,sizeof(quint32)); }
else { qToBigEndian( n ,bytes); return io->putChar(char(majorType << cbor::MajorTypeShift) | cbor::Value64Bit) && io->write(bytes,sizeof(quint64)); }
}
if (n < 24 ) { return putMajorValue(cbor::MajorValue(n) , majorType) ; } else
if (n <= 0xff ) { return Q_LIKELY(putMajorValue(cbor::NextByteUnsignedInt , majorType)) && putBigEndian(quint8 (n)); } else
if (n <= 0xffff ) { return Q_LIKELY(putMajorValue(cbor::Next16BitUnsignedInt, majorType)) && putBigEndian(quint16(n)); } else
if (n <= 0xffffffffL) { return Q_LIKELY(putMajorValue(cbor::Next32BitUnsignedInt, majorType)) && putBigEndian(quint32(n)); } else
{ return Q_LIKELY(putMajorValue(cbor::Next64BitUnsignedInt, majorType)) && putBigEndian(quint64(n)); }
}
template<typename T> inline bool putBigEndian(T&& t) {
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
return io->write(reinterpret_cast<char *>(&t), sizeof(t));
#else
using TValue = typename std::remove_reference<T>::type;
if (sizeof(TValue)==1) { return io->putChar( char(t )); }
if (sizeof(TValue)==2) { union { TValue t; quint16 bytes; } u = { t }; u.bytes = qbswap(u.bytes); return io->write(reinterpret_cast<char *>(&u),sizeof(TValue)); }
if (sizeof(TValue)==4) { union { TValue t; quint32 bytes; } u = { t }; u.bytes = qbswap(u.bytes); return io->write(reinterpret_cast<char *>(&u),sizeof(TValue)); }
if (sizeof(TValue)==8) { union { TValue t; quint64 bytes; } u = { t }; u.bytes = qbswap(u.bytes); return io->write(reinterpret_cast<char *>(&u),sizeof(TValue)); }
return false;
#endif
}
QIODevice* io = nullptr;
QStack<bool> levels = QStack<bool>(); //!< minimal dynamic context to ensure well-formedness in case TResult is abandoned
QIODevice* io = nullptr;
std::vector<bool> levels; //!< minimal dynamic context to ensure well-formedness in case TResult is abandoned: true indicates Definite levels with prefixed size
};
template<> struct BindSupport<CborWriter,const char*&> : BindNative {};
template<> struct BindSupport<CborWriter, bool&> : BindNative {};
template<> struct BindSupport<CborWriter, float&> : BindNative {};
template<> struct BindSupport<CborWriter, double&> : BindNative {};
template<> struct BindSupport<CborWriter,const char*> : BindNative {};
template<> struct BindSupport<CborWriter,QByteArray&> : BindNative {};
template<> struct BindSupport<CborWriter, QString&> : BindNative {};
template<> struct BindSupport<CborWriter, bool&> : BindNative {};
template<> struct BindSupport<CborWriter, float&> : BindNative {};
template<> struct BindSupport<CborWriter, double&> : BindNative {};
template<typename T> struct BindSupport<CborWriter,T&,typename std::enable_if<std::is_integral<T>::value>::type> : BindNative {};
//template bool CborWriter::_bind<qulonglong&>(qulonglong& v);
//template bool CborWriter::_bind< qlonglong&>( qlonglong& v);
#if QT_VERSION>=QT_VERSION_CHECK(5,12,0)
// //////////////////////////////////////////////////////////////////////////
// QBind<QCborValue,T> support
#include <QtCore/qcborvalue.h>
// TODO
#endif
......@@ -42,76 +42,66 @@
#include <type_traits>
#include <QtCore/qdatastream.h>
#include <QtCore/qstack.h>
#include <QtCore/qvariant.h>
#include "QBind_impl.h"
// //////////////////////////////////////////////////////////////////////////
// QBind<QDataStream*,T> support
// QBind<QDataStream*,T> support using operator<< for all registered types and containers, or GenericBind with DataTag
class DataStreamWriter : public QMovedWriter<DataStreamWriter>
// \warning As with QDataStream, writers and readers must:
// - bind the same C++ root data type (when unknown, QVariant can be used on both side)
// - use the same set of registered QMetaTypeIds with operator<< defined
// - use compatible ByteOrder, FloatingPointPrecision, and Version
class DataWriter : public QMovedWriter<DataWriter>
{
protected:
DataStreamWriter(const DataStreamWriter &o) : QMovedWriter(), io(o.io) {}
DataStreamWriter &operator=(const DataStreamWriter &) = delete;
DataWriter(const DataWriter &o) : QMovedWriter<DataWriter>(), io(o.io) {}
DataWriter &operator=(const DataWriter &) = delete;
public:
Q_ENABLE_MOVE(DataStreamWriter, std::swap(io, o.io); )
DataStreamWriter(QDataStream* io) : io(io) { Q_ASSERT(io); }
Q_ENABLE_MOVE(DataWriter, std::swap(io, o.io); )
DataWriter(QDataStream* io) : io(io) { Q_ASSERT(io); }
operator bool() { return io->status()==QDataStream::Ok; } // for QMovedWriter
operator bool() { return io && io->status()==QDataStream::Ok; } // for QMovedWriter
protected:
friend class QMovedWriter<DataStreamWriter>;
friend class QMovedWriter<DataWriter>;
template<class T_> friend class Val; // calls methods below
bool _sequence(quint32* rows=nullptr) { if (rows) *io << *rows; return true; }
bool _record (quint32* cols=nullptr) { Q_UNUSED(cols) ; return true; }
bool _null ( ) { return true; }
bool _bind ( const char* s) { *io << s; return true; }
bool _bind ( QString& s) { *io << s; return true; }
bool _bind ( bool& b) { *io << b; return true; }
bool _bind ( float& f) { *io << f; return true; }
bool _bind ( double& d) { *io << d; return true; }
bool _bind ( qint32& i) { *io << i; return true; }
bool _bind ( quint32& u) { *io << u; return true; }
// TODO BindNative all types in http://doc.qt.io/qt-5/datastreamformat.html
bool _sequence(quint32* rows=nullptr) { if (rows) *io << *rows; return true; }
bool _record (quint32* cols=nullptr) { if (cols) *io << *cols; return true; }
bool _null ( ) { *io << nullptr; return true; }
template<typename T>
bool _bind ( T& t) { *io << t; return true; } // Use QVariant to explicit the parts that may be unknown to the reader
bool _bind ( const char* s) { QByteArray ba = QByteArray::fromRawData(s, int(qstrlen(s))); return _bind(ba); }
template<class T_> friend class Seq; // calls methods below
template<class T_> friend class Rec; // calls methods below
bool _item(QByteArray& k) { Q_UNUSED(k); return true; }
bool _item(QByteArray& k) { Q_UNUSED(k); return true; } // record keys are implicit, maps should be serialized as a sequence of records with key and value items
bool _item( ) { return true; }
bool _out( ) { return true; }
private:
QDataStream* io;
};
template<> struct BindSupport<DataStreamWriter,const char*> : BindNative {};
template<> struct BindSupport<DataStreamWriter, QString&> : BindNative {};
template<> struct BindSupport<DataStreamWriter, bool&> : BindNative {};
template<> struct BindSupport<DataStreamWriter, float&> : BindNative {};
template<> struct BindSupport<DataStreamWriter, double&> : BindNative {};
template<> struct BindSupport<DataStreamWriter, qint32&> : BindNative {};
template<> struct BindSupport<DataStreamWriter, quint32&> : BindNative {};
// TODO BindNative all types in http://doc.qt.io/qt-5/datastreamformat.html
template<> struct BindSupport<DataWriter,const char*> : BindNative {};
template<> struct BindSupport<DataWriter, QString&> : BindNative {};
template<> struct BindSupport<DataWriter,QByteArray&> : BindNative {};
template<> struct BindSupport<DataWriter, bool&> : BindNative {};
template<> struct BindSupport<DataWriter, float&> : BindNative {};
template<> struct BindSupport<DataWriter, double&> : BindNative {};
template<> struct BindSupport<DataWriter, qint32&> : BindNative {};
template<> struct BindSupport<DataWriter, quint32&> : BindNative {};
template<> struct BindSupport<DataWriter, qlonglong&> : BindNative {};
template<> struct BindSupport<DataWriter,qulonglong&> : BindNative {};
template<typename T> struct BindSupport<DataWriter,T&,typename std::enable_if<
QtPrivate::IsAssociativeContainer<typename std::remove_reference<typename std::remove_cv<T>::type>::type>::Value // Not handled correctly by _record()._item(k) which makes the keys implicit
>::type
> : BindNative {};
// TODO BindNative all types for which T stream operators are defined : QMetaTypeId<typename std::remove_reference<typename std::remove_cv<T>>>::Defined ?
// --------------------------------------------------------------------------
// It is not possible to provide a general QDataStreamReader because the QDataStream structure is implicit:
// A reader is assumed to specify exactly what to read based on the stream origin and version
// For instance, it is not possible to implement a generic _bind
// bool _bind() { Q_ASSERT_X(false,Q_FUNC_INFO,"Cannot Read unknown type"); return false; }
// Moreover, implementing _item and _out would require inserting special bytes, making the stream more akin to Cbor:
// enum DataTag : quint8 { Unknown = 0x00, Null = 0xBB, Record = 0xCC, Sequence = 0xDD, Item = 0xEE, Out = 0xFF };
// bool _item() { return get(Item); }
// bool _out () { return get(Out ); }
// bool get(DataTag expected) {
// if (nextTag==Unknown) {
// io >> nextTag;
// }
// if (nextTag==expected) {
// nextTag= Unknown;
// return true;
// }
// return false;
// }
// QVariant can be used for parts which type are not known in advance
......@@ -52,6 +52,8 @@
// //////////////////////////////////////////////////////////////////////////
// QBind<QJson*,T> support
// TODO Only for QCborValue...
class QJsonBuilderImpl;
class QJsonBuilder : public QScopedResult<QJsonBuilder, QJsonBuilderImpl, BindMode::Write>
{
......@@ -205,7 +207,7 @@ private:
friend class QJsonWriterImpl; // uses method below
QJsonWriter(QJsonWriterImpl* outer) : QScopedResult(outer, false) {}
};
class QJsonWriterImpl
class QJsonWriterImpl : public IWriter
{
Q_DISABLE_COPY(QJsonWriterImpl)
public:
......@@ -234,6 +236,9 @@ protected:
template<typename T> bool _bind(T t, typename std::enable_if<std::is_integral<T>::value && std:: is_signed<T>::value>::type* =nullptr) {
return io->write(QByteArray::number( qlonglong(t))); }
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
......@@ -272,7 +277,7 @@ private:
}
QIODevice* io;
QStack<Step> levels = QStack<Step>(); //!< minimal dynamic context to implement out() and ensure well-formedness in case TResult is abandoned
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 {};
......
/****************************************************************************
* **
* ** Copyright (C) 2017 MinMaxMedical.
* ** All rights reserved.
* ** Contact: MinMaxMedical <InCAS@MinMaxMedical.com>
* **
* ** This file is part of the modmedLog module.
* **
* ** $QT_BEGIN_LICENSE:BSD$
* ** You may use this file under the terms of the BSD license as follows:
* **
* ** "Redistribution and use in source and binary forms, with or without
* ** modification, are permitted provided that the following conditions are
* ** met:
* ** * Redistributions of source code must retain the above copyright
* ** notice, this list of conditions and the following disclaimer.
* ** * Redistributions in binary form must reproduce the above copyright
* ** notice, this list of conditions and the following disclaimer in
* ** the documentation and/or other materials provided with the
* ** distribution.
* ** * Neither the name of MinMaxMedical S.A.S. and its Subsidiary(-ies) nor
* ** the names of its contributors may be used to endorse or promote
* ** products derived from this software without specific prior written