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

partial solution to:

- remove QBind::bind::overloads that do not allow both definitions to
coexist
- handle T&&, T& and const T& universally using perfect forwarding,
- factor out static_assert when QBind Read to const T&,
- factor out QBind Write const_cast<T&>
parent 17b56280
......@@ -78,6 +78,41 @@ private:
T& t;
};
// //////////////////////////////////////////////////////////////////////////
// QBind<TResult,T>
#include <type_traits>
template<class T_> class Val;
enum BindMode { Read=0, Write=1 }; //!< drives QBind::bind() traversal and processing (the design would support others like Append)
template<class TResult> using IsReader = typename std::enable_if<TResult::Mode==Read >::type;
template<class TResult> using IsWriter = typename std::enable_if<TResult::Mode==Write>::type;
template<class TResult, typename T, class TEnabledIf=void> // least specialized definition
struct QBind { static TResult bind(Val<TResult> value, T t) {
return t.bind(std::move(value)); // In case of error, define a T::bind(Val<TResult>) method or external QBind<TResult,Mode,T>::bind(Val<TResult>,T)
}};
// NB: QBind has a structure similar to a Traversable : https://wiki.haskell.org/Typeclassopedia#Traversable
// A QTraverse<Val<TResult>>,T> would be equivalent but would not mandate to actually traverse T to get from Val<TResult> to the actual TResult
// A QFold<TResult,T> would be equivalent to a Foldable allowing to fold T to any TResult without mandating a common Val/Seq structure
template<class TResult, typename T>
struct QBind<TResult, T&&, IsWriter<TResult>> { static TResult bind(Val<TResult> dst, T&& src) {
return dst.bind(src); // T&
}};
template<class TResult, typename T>
struct QBind<TResult, const T&, IsWriter<TResult>> { static TResult bind(Val<TResult> value, const T& t) {
return value.bind(const_cast<T&>(t)); // T is not natively supported by TResult but t will not be modified anyway
}};
template<class TResult, typename T>
struct QBind<TResult, const T&, IsReader<TResult>> { static TResult bind(Val<TResult>, const T&) {
static_assert(TResult::Mode==Write,"Cannot Read from TResult into const T&"); return false;
}};
// //////////////////////////////////////////////////////////////////////////
// Generic fluent interface for traversing structured values and processing them along the way:
// - serializing, deserializing
......@@ -85,7 +120,9 @@ private:
// - etc...
// See Person::bind() definition below for an example
enum BindMode { Read, Write }; //!< drives QBind<TResult,T>() traversal and processing (the design would support others like Append)
struct BindNative {};
struct BindGeneric {};
template<class TImpl, typename T, typename TEnabledIf=void> struct BindSupport : BindGeneric {};
template<class T_> class Rec; //!< a Record data structure (not defined in this Proof-Of-Concept for the sake of simplicity)
template<class T_> class Seq; //!< a Sequence data structure defined below
......@@ -103,12 +140,13 @@ public:
operator bool() { return m_out.operator bool(); } //!< to drive QBind<TResult,T>() traversal
TImpl* operator->() { return m_out.operator ->(); }
/**/ Seq<T_> sequence() { if (Q_LIKELY(m_out) && m_out-> _sequence()) return Seq<T_>(std::move(m_out)); return Seq<T_>(); }
/**/ T_ null() { if (Q_LIKELY(m_out) && m_out-> _null()) return std::move(m_out) ; return T_ (); }
template<typename T> T_ bind(const T& t) { if (Q_LIKELY(m_out) && m_out-> _bind(t)) return std::move(m_out) ; return T_ (); }
template<typename T> T_ bind( T& t) { if (Q_LIKELY(m_out) && m_out-> _bind(t)) return std::move(m_out) ; return T_ (); }
template<typename T> T_ bind( T&& t) { if (Q_LIKELY(m_out) && m_out-> _bind(t)) return std::move(m_out) ; return T_ (); }
template<typename T> T_ bind( T&& t) { if (_bind(std::forward<T>(t),BindSupport<TImpl,T>())) return std::move(m_out) ; else return T_ (); }
/**/ T_ null () { if (Q_LIKELY(m_out) && m_out-> _null()) return std::move(m_out) ; else return T_ (); }
/**/ Seq<T_> sequence () { if (Q_LIKELY(m_out) && m_out-> _sequence()) return Seq<T_>(std::move(m_out)); else return Seq<T_>(); }
private:
template<typename T> bool _bind(T&& t, BindNative ) { return Q_LIKELY(m_out) && m_out->_bind( std::forward<T>(t)); }
template<typename T> bool _bind(T&& t, BindGeneric) { return QBind<TResult,T&&>::bind(m_out.value(),std::forward<T>(t)); }
T_ m_out = T_(); //!< moved context of current traversal up to TResult that will reference the val itself (be it a QIODevice or QCborValue)
};
......@@ -120,8 +158,8 @@ public:
Seq(T_&& out) { std::swap(m_out, out); }
// T_ can be either a TResult or any combination of nested Seq or Rec like Rec<Seq<TResult>
using TResult = typename T_::TResult; //!< the start and end point of the traversal as well as the associated processing (see QJsonWriter for an example)
using TImpl = typename TResult::TImpl;
using TResult = typename T_::TResult; //!< the start and end point of the traversal as well as the associated processing (see QJsonWriter for an example)
using TImpl = typename TResult::TImpl;
operator bool() { return m_out.operator bool(); } //!< to drive QBind<TResult,T>() traversal
TImpl* operator->() { return m_out.operator ->(); }
......@@ -129,15 +167,14 @@ public:
Val<Seq<T_>> item(int min= 1) { if (Q_LIKELY(m_out) && m_out->_item(min)) return Val<Seq<T_>>(std::move(*this)); return Val<Seq<T_>>(); }
/**/ T_ out(int max=-1) { if (Q_LIKELY(m_out) && m_out-> _out(max)) return std::move(m_out) ; return T_ (); }
operator TResult() { return out(); /* recursively calls operator TResult() as mandated by T_ */ }
TResult result() { return operator TResult(); }
operator TResult() { return out(); /* recursively calls operator TResult() as mandated by T_ */ }
TResult result() { return operator TResult(); }
Val<TResult> value() { return m_out.value(); }
// Shortcuts
template<typename T> Seq<T_> bind(const T& t) { if (itemValue().bind(t)) return std::move(*this); return Seq<T_>(); } // binding Val<T_> instead of Val<Seq<T_>> requires less code generation
template<typename T> Seq<T_> bind( T& t) { if (itemValue().bind(t)) return std::move(*this); return Seq<T_>(); }
template<typename T> Seq<T_> bind( T&& t) { if (itemValue().bind(t)) return std::move(*this); return Seq<T_>(); }
template<typename T> Seq<T_> bind(T&& t) { if (itemValue().bind(std::forward<T>(t))) return std::move(*this); return Seq<T_>(); }
protected:
template<class TResult, typename T> friend class QBind;
template<class TResult, typename T, typename TEnabledIf> friend class QBind;
Val<T_> itemValue(int min= 1) { if (Q_LIKELY(m_out) && m_out->_item(min)) { T_ scopedT(m_out); return std::move(Val<T_>(std::move(scopedT))); } return Val<T_>(); } // the m_out copy must be scoped and will be invalid when the original is destroyed
private:
T_ m_out = T_();
......@@ -161,17 +198,14 @@ public:
using TImpl = TImpl_ ;
static constexpr BindMode Mode = Mode_;
operator bool() { return m; } //!< to drive QBind<TResult,T>() traversal
TImpl* operator->() { Q_ASSERT_X(m,Q_FUNC_INFO,"check operator bool() before calling operator->()"); return m; }
operator bool() { return m; } //!< to drive QBind<TResult,T>() traversal
TImpl* operator->() { Q_ASSERT_X(m,Q_FUNC_INFO,"check operator bool() before calling operator->()"); return m; }
Val<TResult> value() { return Val<TResult>(std::move(*static_cast<TResult*>(this))); }
// Shortcuts
/**/ Seq<TResult> sequence() { return value().sequence(); }
/**/ TResult null() { return value(). null(); }
template<typename T> TResult bind(const T& t) { return value(). bind(t); }
template<typename T> TResult bind( T& t) { return value(). bind(t); }
template<typename T> TResult bind( T&& t) { return value(). bind(t); }
template<typename T> TResult bind(T&& t) { return value().bind(std::forward<T>(t)); }
/**/ TResult null () { return value().null (); }
/**/ Seq<TResult> sequence () { return value().sequence (); }
void reportError(const char* error) { if (m) m->reportError(error); }
protected:
......@@ -197,16 +231,13 @@ public:
using TImpl = TResult_;
static constexpr BindMode Mode = BindMode::Write;
TImpl* operator->() { return static_cast<TImpl*>(this); }
TImpl* operator->() { return static_cast<TImpl*>(this); }
Val<TResult> value() { return Val<TResult>(std::move(*static_cast<TResult*>(this))); }
// Shortcuts
/**/ Seq<TResult> sequence() { return value().sequence(); }
/**/ TResult null() { return value(). null(); }
template<typename T> TResult bind(const T& t) { return value(). bind(t); }
template<typename T> TResult bind( T& t) { return value(). bind(t); }
template<typename T> TResult bind( T&& t) { return value(). bind(t); }
template<typename T> TResult bind(T&& t) { return value().bind(std::forward<T>(t)); }
/**/ TResult null () { return value().null (); }
/**/ Seq<TResult> sequence () { return value().sequence (); }
void reportError(const char*) {} // a TResult with BindMode::Write will not encounter bind mismatches but only write errors independent from the current state
protected:
......@@ -220,9 +251,7 @@ template<class TWriter>
class Utf8Result
{
public:
template<typename T> Utf8Result(const T& t) : m_writer(&m_result) { m_result.open(QIODevice::WriteOnly); m_writer.bind(t); }
template<typename T> Utf8Result( T&& t) : m_writer(&m_result) { m_result.open(QIODevice::WriteOnly); m_writer.bind(t); }
template<typename T> Utf8Result(T&& t) : m_writer(&m_result) { m_result.open(QIODevice::WriteOnly); m_writer.bind(std::forward<T>(t)); }
operator QByteArray() const { return m_result.buffer(); }
private:
QBuffer m_result;
......@@ -230,150 +259,81 @@ private:
};
// //////////////////////////////////////////////////////////////////////////
// QBind<TResult,T>
#include <type_traits>
template<class TResult, typename T>
struct QBind { static TResult bind(Val<TResult> value, T& t) {
return t.bind(std::move(value)); // In case of error, define a T::bind(Val<TResult>) method or an external QBind<TResult,T>::bind(Val<TResult>,T)
}};
// NB: QBind has a structure similar to a Traversable : https://wiki.haskell.org/Typeclassopedia#Traversable
// A QTraverse<Val<TResult>>,T> would be equivalent but would not mandate to actually traverse T to get from Val<TResult> to the actual TResult
// A QFold<TResult,T> would be equivalent to a Foldable allowing to fold T to any TResult without mandating a common Val/Seq structure
template<class TResult, typename T>
struct QBind<TResult, const T> { static TResult bind(Val<TResult> value, const T& t) {
static_assert(TResult::Mode==Write,"Cannot Read from TResult into const T&");
return t.bind(std::move(value)); // In case of error, define a T::bind(Val<TResult>) method or an external QBind<TResult,T>::bind(Val<TResult>,T)
}};
// --------------------------------------------------------------------------
// QBind partial specializations (generic on TResult, usually not on TResult::Mode for dynamically-sized or builtin types)
#include <cstddef>
template<class TResult>
struct QBind<TResult, std::nullptr_t> { static TResult bind(Val<TResult> value, std::nullptr_t&) {
struct QBind<TResult, std::nullptr_t&> { static TResult bind(Val<TResult> value, std::nullptr_t&) {
return value.null();
}};
template<class TResult>
struct QBind<TResult, const bool> { static TResult bind(Val<TResult> dst, const bool& src) {
static_assert(TResult::Mode==Write,"Cannot Read from TResult into const bool&");
struct QBind<TResult, bool&, IsWriter<TResult>> { static TResult bind(Val<TResult> dst, bool& src) {
return dst.bind(src ? "true" : "false");
}};
template<class TResult>
struct QBind<TResult, bool> {
static TResult bind(Val<TResult> dst, bool& src, std::enable_if_t<TResult::Mode==Write>* = nullptr) {
return dst.bind(src ? "true" : "false");
struct QBind<TResult, bool&, IsReader<TResult>> { static TResult bind(Val<TResult> src, bool& dst) {
QByteArray ba;
auto r = src.bind(ba);
if (r) {
if (ba.toLower()=="true" ) dst = true ;
else if (ba.toLower()=="false") dst = false;
else r.reportError(qBindExpectedBoolean);
}
// C++17 constexpr if is required to handle situations where one of these definitions is not used
static TResult bind(Val<TResult> src, bool& dst, std::enable_if_t<TResult::Mode==Read >* = nullptr) {
QByteArray ba;
TResult r = src.bind(ba);
if (r) {
if (ba.toLower()=="true" ) dst = true ;
else if (ba.toLower()=="false") dst = false;
else r.reportError(qBindExpectedBoolean);
}
return r;
}
};
return r;
}};
template<class TResult>
struct QBind<TResult, const int> { static TResult bind(Val<TResult> dst, const int& src) {
static_assert(TResult::Mode==Write,"Cannot Read from TResult into const int&");
struct QBind<TResult, int&, IsWriter<TResult>> { static TResult bind(Val<TResult> dst, int& src) {
char n[1+std::numeric_limits<int>::digits10+1];
itoa(src, n, 10);
return dst.bind(static_cast<const char*>(n));
}};
template<class TResult>
struct QBind<TResult, int> {
static TResult bind(Val<TResult> dst, int& src, std::enable_if_t<TResult::Mode==Write>* = nullptr) {
char n[1+std::numeric_limits<int>::digits10+1];
itoa(src, n, 10);
return dst.bind(static_cast<const char*>(n));
}
};
template<class TResult>
struct QBind<TResult, const double> { static TResult bind(Val<TResult> dst, const double& src) {
static_assert(TResult::Mode==Write,"Cannot Read from TResult into const double&");
struct QBind<TResult, double&, IsWriter<TResult>> { static TResult bind(Val<TResult> dst, double& src) {
// 17 = std::numeric_limits<double>::max_digits10
char n[1+std::numeric_limits<double>::digits10+1+17+1+1+std::numeric_limits<double>::max_exponent10+1];
sprintf(n, "%.17g", src);
return dst.bind(static_cast<const char*>(n));
}};
template<class TResult>
struct QBind<TResult, double> {
static TResult bind(Val<TResult> dst, double& src, std::enable_if_t<TResult::Mode==Write>* = nullptr) {
char n[1+std::numeric_limits<double>::digits10+1+17+1+1+std::numeric_limits<double>::max_exponent10+1];
sprintf(n, "%.17g", src);
return dst.bind(static_cast<const char*>(n));
}
};
template<class TResult>
struct QBind<TResult, const float> { static TResult bind(Val<TResult> dst, const float& src) {
static_assert(TResult::Mode==Write,"Cannot Read from TResult into const float&");
struct QBind<TResult, float&, IsWriter<TResult>> { static TResult bind(Val<TResult> dst, float& src) {
// 8 = std::numeric_limits<float>::max_digits10
char n[1+std::numeric_limits<float>::digits10+1+8+1+1+std::numeric_limits<float>::max_exponent10+1];
sprintf(n, "%.8g", src);
return dst.bind(static_cast<const char*>(n));
}};
template<class TResult>
struct QBind<TResult, float> {
static TResult bind(Val<TResult> dst, float& src, std::enable_if_t<TResult::Mode==Write>* = nullptr) {
char n[1+std::numeric_limits<float>::digits10+1+8+1+1+std::numeric_limits<float>::max_exponent10+1];
sprintf(n, "%.8g", src);
return dst.bind(static_cast<const char*>(n));
}
};
#include <QtCore/qbytearray.h>
template<class TResult>
struct QBind<TResult, const QByteArray> { static TResult bind(Val<TResult> dst, const QByteArray& src) {
static_assert(TResult::Mode==Write,"Cannot Read from TResult into const QByteArray&");
struct QBind<TResult, QByteArray&, IsWriter<TResult>> { static TResult bind(Val<TResult> dst, QByteArray& src) {
return dst.bind(src.constData());
}};
template<class TResult>
struct QBind<TResult, QByteArray> {
static TResult bind(Val<TResult> dst, QByteArray& src, std::enable_if_t<TResult::Mode==Write>* = nullptr) {
return dst.bind(src.constData());
}
};
#include <QtCore/qstring.h>
template<class TResult>
struct QBind<TResult, const QString> { static TResult bind(Val<TResult> dst, const QString& src) {
static_assert(TResult::Mode==Write,"Cannot Read from TResult into const QString&");
struct QBind<TResult, QString&, IsWriter<TResult>> { static TResult bind(Val<TResult> dst, QString& src) {
return dst.bind(src.toUtf8().constData());
}};
template<class TResult>
struct QBind<TResult, QString> {
static TResult bind(Val<TResult> dst, QString& src, std::enable_if_t<TResult::Mode==Write>* = nullptr) {
return dst.bind(src.toUtf8().constData());
}
// C++17 constexpr if is required to handle situations where one of these definitions is not used
static TResult bind(Val<TResult> src, QString& dst, std::enable_if_t<TResult::Mode==Read >* = nullptr) {
QByteArray ba;
auto result = src.bind(ba);
if (result)
dst = QString::fromUtf8(ba);
return result;
struct QBind<TResult, QString&, IsReader<TResult>> { static TResult bind(Val<TResult> src, QString& dst) {
QByteArray ba;
auto r = src.bind(ba);
if (r) {
dst = QString::fromUtf8(ba);
}
};
return r;
}};
#include <QtCore/qvector.h>
template<class TResult, typename T>
struct QBind<TResult, const QVector<T>> { static TResult bind(Val<TResult> dst, const QVector<T>& src) {
static_assert(TResult::Mode==Write,"Cannot Read from TResult into const QVector<T>&");
struct QBind<TResult, QVector<T>&, IsWriter<TResult>> { static TResult bind(Val<TResult> dst, QVector<T>& src) {
auto s(dst.sequence());
for (auto&& t : src) {
s = s.bind(t);
......@@ -381,34 +341,23 @@ struct QBind<TResult, const QVector<T>> { static TResult bind(Val<TResult> dst,
return s;
}};
template<class TResult, typename T>
struct QBind<TResult, QVector<T>> {
static TResult bind(Val<TResult> dst, QVector<T>& src, std::enable_if_t<TResult::Mode==Write>* = nullptr) {
auto s(dst.sequence());
for (auto&& t : src) {
s = s.bind(t);
}
return s;
}
// C++17 constexpr if is required to handle situations where one of these definitions is not used
static TResult bind(Val<TResult> src, QVector<T>& dst, std::enable_if_t<TResult::Mode==Read >* = nullptr) {
auto s(src.sequence());
Val<Seq<TResult>> i;
while (i = s.item(0)) {
T t;
if (s = i.bind(t))
dst.push_back(t);
}
return s;
struct QBind<TResult, QVector<T>&, IsReader<TResult>> { static TResult bind(Val<TResult> src, QVector<T>& dst) {
auto s(src.sequence());
Val<Seq<TResult>> i;
while (i = s.item(0)) {
T t;
if (s = i.bind(t))
dst.push_back(t);
}
};
return s;
}};
// --------------------------------------------------------------------------
// QBind specialization for generic Val<TDst>
template<class TSrcResult, class TDst>
struct QBind<TSrcResult, Val<TDst>> { static TSrcResult bind(Val<TSrcResult> src, Val<TDst>& dst) {
static_assert(TSrcResult::Mode==Read ,"cannot read from TSrcResult");
static_assert(TDst ::Mode==Write,"cannot write to TDst" );
struct QBind<TSrcResult, Val<TDst>&&, IsReader<TSrcResult>> { static TSrcResult bind(Val<TSrcResult> src, Val<TDst>&& dst) {
static_assert(TDst::Mode!=Read,"Cannot Read from TSrcResult to TDst with BindMode==Read");
TSrcResult srcRes;
{
......@@ -421,7 +370,7 @@ struct QBind<TSrcResult, Val<TDst>> { static TSrcResult bind(Val<TSrcResult> src
Val<TSrcResult> srcVal; Val<TDst> dstVal;
while (srcVal = srcSeq.itemValue(0)) { // using item()'s Val<Seq<TSrcResult> would cause an infinite compiler loop to generate corresponding QBind<Val<Seq<Seq<...>>,_> functions
dstVal = dstSeq.itemValue();
srcSeq = QBind<TSrcResult, Val<TDst>>::bind(std::move(srcVal), dstVal);
srcSeq = srcVal.bind(dstVal);
}
/**/ dstSeq.out();
......
......@@ -126,11 +126,6 @@ protected:
char bytes[sizeof(number.bits)]; qToBigEndian(number.bits, bytes);
return (io->putChar((cbor::SimpleTypesType << cbor::MajorTypeShift) | cbor::DoublePrecisionFloat) && io->write(bytes, sizeof(bytes))); }
// Val<TResult> prevents QBind from getting out() of an outer Seq for instance
template<typename T> bool _bind(const T& t) { return QBind<TResult,T>::bind(TResult(*this).value(), const_cast<T&>(t)); } // t will not be modified anyway
template<typename T> bool _bind( T& t) { return QBind<TResult,T>::bind(TResult(*this).value(), t ); }
template<typename T> bool _bind( T&& t) { return QBind<TResult,T>::bind(TResult(*this).value(), t ); }
template<class T_> friend class Seq; // calls methods below
bool _item(int min= 1) { Q_UNUSED(min); return true ; }
......@@ -152,3 +147,7 @@ private:
QIODevice* io = nullptr;
int levels = 0; //!< minimal dynamic context to ensure well-formedness in case TResult is abandoned
};
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 {};
......@@ -82,16 +82,11 @@ protected:
bool _sequence() { levels.push(Step(nullptr)); return true; }
bool _null() { set(QJsonValue( )); return true; }
bool _bind(const char* s) { set(QJsonValue(s)); return true; }
bool _bind(double d) { set(QJsonValue(d)); return true; }
bool _bind(bool b) { set(QJsonValue(b)); return true; }
bool _bind(double d) { set(QJsonValue(d)); return true; }
template<typename T> bool _bind(T t, std::enable_if_t<std::is_arithmetic<T>::value>* = nullptr) { return _bind(double(t)); }
// Val<TResult> prevents QBind from getting out() of an outer Seq for instance
template<typename T> bool _bind(const T& t) { return QBind<QJsonBuilder,T>::bind(QJsonBuilder(this).value(), const_cast<T&>(t)); } // t will not be modified anyway
template<typename T> bool _bind( T& t) { return QBind<QJsonBuilder,T>::bind(QJsonBuilder(this).value(), t ); }
template<typename T> bool _bind( T&& t) { return QBind<QJsonBuilder,T>::bind(QJsonBuilder(this).value(), t ); }
template<class T_> friend class Seq; // calls methods below
bool _item(const char* key) { levels.last().key=key ; return true; }
......@@ -115,6 +110,12 @@ private:
QStack<Step> levels = QStack<Step>(); //!< minimal dynamic context to implement out() and ensure actual building in case QJsonBuilderImpl is abandoned
};
QJsonBuilder::QJsonBuilder(QJsonValue* v) : QScopedResult(new QJsonBuilderImpl(v), true) {}
template<> struct BindSupport<QJsonBuilderImpl,const char*> : BindNative {};
template<> struct BindSupport<QJsonBuilderImpl,bool > : BindNative {};
template<> struct BindSupport<QJsonBuilderImpl,double > : BindNative {};
//template<typename T> struct BindSupport<QJsonBuilderImpl,std::enable_if_t<std::is_arithmetic<T>::value,T>> : BindNative {};
// --------------------------------------------------------------------------
class JsonVisitorImpl;
class QJsonVisitor : public QScopedResult<QJsonVisitor, JsonVisitorImpl, BindMode::Read>
......@@ -164,14 +165,10 @@ protected:
bool _record() { if (current()->isObject()) { steps.push(Step()); return true; } reportError(qBindExpectedRecord ); return false; }
bool _sequence() { if (current()->isArray ()) { steps.push(Step()); return true; } reportError(qBindExpectedSequence); return false; }
bool _bind(QString& v) { if (current()->isString()) { v = current()->toString(); return true; } reportError(qBindExpectedText ); return false; }
bool _bind( double& v) { if (current()->isDouble()) { v = current()->toDouble(); return true; } reportError(qBindExpectedDecimal ); return false; }
bool _bind( bool& v) { if (current()->isBool ()) { v = current()->toBool (); return true; } reportError(qBindExpectedBoolean ); return false; }
bool _bind( double& v) { if (current()->isDouble()) { v = current()->toDouble(); return true; } reportError(qBindExpectedDecimal ); return false; }
//template<typename T> bool _bind(T& t, std::enable_if_t<std::is_arithmetic<T>::value>* = nullptr) { double d; if (_bind(d)) { t = d; return true; } return false; }
// Val<TResult> prevents QBind from getting out() of an outer Seq for instance
template<typename T> bool _bind(T& t) { return QBind<QJsonVisitor,T>::bind(Val<QJsonVisitor>(QJsonVisitor(this)), t); } // TODO check why .value() does not work
template<class T_> friend class Seq; // calls methods below
bool _item(const char* key) { steps.last().key=key; if (!(steps.last().item = current(1)->toObject().value(QString::fromUtf8(steps.last().key))).isUndefined()) { return true; } return false; }
......@@ -186,6 +183,12 @@ private:
bool isChoice = false;
};
QJsonVisitor::QJsonVisitor(const QJsonValue* v) : QScopedResult(new JsonVisitorImpl(v), true) {}
template<> struct BindSupport<JsonVisitorImpl,QString&> : BindNative {};
template<> struct BindSupport<JsonVisitorImpl,bool &> : BindNative {};
template<> struct BindSupport<JsonVisitorImpl,double &> : BindNative {};
// TODO template<> struct BindSupport<JsonVisitorImpl,std::enable_if_t<std::is_arithmetic<T>::value,T&> : BindNative {};
// --------------------------------------------------------------------------
class QJsonWriterImpl;
class QJsonWriter : public QScopedResult<QJsonWriter, QJsonWriterImpl, BindMode::Write>
......@@ -232,11 +235,6 @@ protected:
template<typename T> bool _bind(T t,std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value>* =nullptr) { return _bind(qulonglong(t)); }
template<typename T> bool _bind(T t,std::enable_if_t<std::is_integral<T>::value && std:: is_signed<T>::value>* =nullptr) { return _bind( qlonglong(t)); }
// Val<TResult> prevents QBind from getting out() of an outer Seq for instance
template<typename T> bool _bind(const T& t) { return QBind<QJsonWriter,T>::bind(QJsonWriter(this).value(), const_cast<T&>(t)); } // t will not be modified anyway
template<typename T> bool _bind( T& t) { return QBind<QJsonWriter,T>::bind(QJsonWriter(this).value(), t ); }
template<typename T> bool _bind( T&& t) { return QBind<QJsonWriter,T>::bind(QJsonWriter(this).value(), t ); }
template<class T_> friend class Seq; // calls methods below
bool _item(int min= 1) { Q_UNUSED(min); return io->write(levels.last().sep)==strlen(levels.last().sep) ; levels.last().sep = ","; }
......@@ -248,6 +246,14 @@ private:
QStack<Step> levels = QStack<Step>(); //!< 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<> struct BindSupport<QJsonWriterImpl, qulonglong> : BindNative {};
template<> struct BindSupport<QJsonWriterImpl, qlonglong> : BindNative {};
// --------------------------------------------------------------------------
class QJsonReaderImpl;
class QJsonReader : public QScopedResult<QJsonReader, QJsonReaderImpl, BindMode::Read>
......@@ -371,9 +377,6 @@ protected:
|| _bind(b)
|| _null(); }
// Val<TResult> prevents QBind from getting out() of an outer Seq for instance
template<typename T> bool _bind(T& t) { return QBind<QJsonReader,T>::bind(QJsonReader(this).value(), t); } // TODO this "mode" (min<=0 or mode==Optional=='?') must be hidden in TResult
template<class T_> friend class Seq; // calls methods below
// bool item(QByteArray& key) would require to cache key:value pairs coming out of order wrt to T items() calls
......@@ -453,6 +456,9 @@ private:
}
};
QJsonReader::QJsonReader(QIODevice* io) : QScopedResult(new QJsonReaderImpl(io), true) {}
template<> struct BindSupport<QJsonReaderImpl,QByteArray&> : BindNative {};
template<> struct BindSupport<QJsonReaderImpl,bool &> : BindNative {};
template<> struct BindSupport<QJsonReaderImpl,double &> : BindNative {};
// //////////////////////////////////////////////////////////////////////////
// QBind<TResult,QJson*> support
......@@ -461,8 +467,7 @@ QJsonReader::QJsonReader(QIODevice* io) : QScopedResult(new QJsonReaderImpl(io),
#include <QtCore/qjsonarray.h>
template<class TResult>
struct QBind<TResult, const QJsonValue> { static TResult bind(Val<TResult> dst, const QJsonValue& src) {
static_assert(TResult::Mode==Write,"Cannot Read from TResult into const QJsonValue&");
struct QBind<TResult, QJsonValue&, IsWriter<TResult>> { static TResult bind(Val<TResult> dst, QJsonValue& src) {
if (src.isArray ()) return dst.bind(src.toArray ());
if (src.isBool ()) return dst.bind(src.toBool ());
if (src.isDouble()) return dst.bind(src.toDouble());
......@@ -470,57 +475,38 @@ struct QBind<TResult, const QJsonValue> { static TResult bind(Val<TResult> dst,
return dst.null();
}};
template<class TResult>
struct QBind<TResult, QJsonValue> {
static TResult bind(Val<TResult> dst, QJsonValue& src, std::enable_if_t<TResult::Mode==Write>* = nullptr) {
if (src.isArray ()) return dst.bind(src.toArray ());
if (src.isBool ()) return dst.bind(src.toBool ());
if (src.isDouble()) return dst.bind(src.toDouble());
if (src.isString()) return dst.bind(src.toString());
return dst.null();
}
static TResult bind(Val<TResult> src, QJsonValue& dst, std::enable_if_t<TResult::Mode==Read >* = nullptr) {
TResult r;
{
ScopedChoice<Val<TResult>> choice(src);
bool b; if (r = src.bind(b)) { dst = QJsonValue(b); return r; }
double d; if (r = src.bind(d)) { dst = QJsonValue(d); return r; }
QString t; if (r = src.bind(t)) { dst = QJsonValue(t); return r; }
QJsonArray a; if (r = src.bind(a)) { dst = a ; return r; }
}
if (!(r = src.null())) r.reportError("Expected bool|double|QString|QJsonArray|null");
/**/ dst = QJsonValue();
return r;
struct QBind<TResult, QJsonValue&, IsReader<TResult>> { static TResult bind(Val<TResult> src, QJsonValue& dst) {
TResult r;
{
ScopedChoice<Val<TResult>> choice(src);
bool b; if (r = src.bind(b)) { dst = QJsonValue(b); return r; }
double d; if (r = src.bind(d)) { dst = QJsonValue(d); return r; }
QString t; if (r = src.bind(t)) { dst = QJsonValue(t); return r; }
QJsonArray a; if (r = src.bind(a)) { dst = a ; return r; }
}
};
if (!(r = src.null())) r.reportError("Expected bool|double|QString|QJsonArray|null");
/**/ dst = QJsonValue();
return r;
}};
#include <QtCore/qjsonarray.h>
template<class TResult>
struct QBind<TResult, const QJsonArray> { static TResult bind(Val<TResult> dst, const QJsonArray& src) {