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

WIP Added Rec

parent 916ea964
......@@ -125,7 +125,7 @@ template<class TImpl, typename T, typename TEnabledIf=void> struct BindSupport :
template<class TImpl> struct BindSupport<TImpl,void,void> : 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 Rec; //!< a Record data structure defined below
template<class T_> class Seq; //!< a Sequence data structure defined below
template<class T_> class Val //!< a choice of sequence(), null(), or any value with a textual representation
......@@ -143,6 +143,7 @@ public:
/**/ 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_>(); }
/**/ Rec<T_> record () { if (Q_LIKELY(m_out) && m_out-> _record()) return Rec<T_>(std::move(m_out)); else return Rec<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_ (); }
private:
......@@ -178,6 +179,7 @@ public:
/**/ Seq<T_> any () { return item().any (); }
/**/ Seq<T_> null () { return item().null (); }
/**/ Seq<Seq<T_>> sequence () { return item().sequence (); }
/**/ Rec<Seq<T_>> record () { return item().record (); }
template<typename T> Seq<T_> bind(T&& t) { return item().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
......@@ -191,6 +193,45 @@ private:
T_ m_out = T_();
};
template<class T_> class Rec
{
Q_DISABLE_COPY(Rec)
public:
Q_ENABLE_MOVE_DEFAULT(Rec)
Rec(T_&& out) { std::swap(m_out, out); }
// T_ can be either a TResult or any combination of nested Seq or Rec like Seq<Rec<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;
operator bool() { return m_out.operator bool(); } //!< to drive QBind<TResult,T>() traversal
TImpl* operator->() { return m_out.operator ->(); }
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_>>(); }
Val<Rec<T_>> item(const char* key) { QByteArray k(key); if (Q_LIKELY(m_out) && m_out->_item(k )) 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
/**/ Rec<T_> any (const char* key ) { return item(key).any (); }
/**/ Rec<T_> null (const char* key ) { return item(key).null (); }
/**/ Seq<Rec<T_>> sequence(const char* key ) { return item(key).sequence (); }
/**/ Rec<Rec<T_>> record (const char* key ) { return item(key).record (); }
template<typename T> Rec<T_> bind (const char* key, T&& t) { return item(key).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>(); }
friend class Val<Rec<T_>>;
friend class Seq<Rec<T_>>;
friend class Rec<Rec<T_>>;
TResult _unsafeCopy() { return m_out._unsafeCopy(); } //!< it is the caller's responsibility to use it immediately and only one time, otherwise the backing TResult state will be unknown
T_ m_out = T_();
};
//! Base class for TResult classes that require a single heap allocation (and lock), but:
//! - are simple to implement
//! - hide implementation methods from the fluent interface
......@@ -218,6 +259,7 @@ public:
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 (); }
/**/ Rec<TResult> record () { return value().record (); }
void reportError(const char* error) { if (m) m->reportError(error); }
protected:
......@@ -256,6 +298,7 @@ public:
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 (); }
/**/ Rec<TResult> record () { return value().record (); }
void reportError(const char*) {} // a TResult with BindMode::Write will not encounter bind mismatches but only write errors independent from the current state
protected:
......@@ -299,6 +342,11 @@ struct QBind<TResult, void, IsReader<TResult>> { static TResult bind(Val<TResult
return srcSeq.out();
}
Rec<TResult> srcRec;
if (srcRec = src.record()) {
return srcRec.out();
}
QString t; if (srcRes = src.bind(t)) { return srcRes; }
}
return src.null();
......@@ -420,6 +468,20 @@ struct QBind<TSrcResult, Val<TDst>&&, IsReader<TSrcResult>> { static TSrcResult
return srcSeq.out();
}
Rec<TSrcResult> srcRec; Rec<TDst> dstRec;
if ( srcRec = src.record()) {
/**/ dstRec = dst.record();
QByteArray srcKey; Val<TSrcResult> srcVal; Val<TDst> dstVal;
while (srcVal = srcRec._unsafeItem(srcKey)) {
dstVal = dstRec._unsafeItem(srcKey);
srcRec = srcVal.bind(std::move(dstVal));
}
/**/ dstRec.out();
return srcRec.out();
}
bool b; if (srcRes = src.bind(b)) { dst.bind(b); return srcRes; }
double d; if (srcRes = src.bind(d)) { dst.bind(d); return srcRes; }
// TODO other native types we do not want to treat as text: qlonglong, qulonglong, float (to optimize CborWriter output)
......
......@@ -55,6 +55,7 @@ enum MajorTypes {
NegativeIntegerType = 1,
TextStringType = 3,
ArrayType = 4,
MapType = 5,
SimpleTypesType = 7
};
......@@ -83,6 +84,7 @@ enum {
enum : unsigned char {
BreakByte = (unsigned)Break | (SimpleTypesType << MajorTypeShift),
IndefiniteLengthMapByte = IndefiniteLength | (MapType << MajorTypeShift),
IndefiniteLengthArrayByte = IndefiniteLength | (ArrayType << MajorTypeShift),
NullByte = NullValue | (SimpleTypesType << MajorTypeShift),
TrueByte = TrueValue | (SimpleTypesType << MajorTypeShift),
......@@ -117,6 +119,7 @@ protected:
bool _null() { return io->putChar(cbor:: NullByte) ; }
bool _sequence() { levels++; return io->putChar(cbor::IndefiniteLengthArrayByte) ; }
bool _record() { levels++; return io->putChar(cbor::IndefiniteLengthMapByte ) ; }
bool _bind(const char* s) { return (putInteger(cbor::TextStringType, strlen(s)) && io->write(s)); }
// BindNative overloads
......@@ -138,9 +141,11 @@ protected:
: putInteger(cbor::UnsignedIntegerType, quint64( t)); }
template<class T_> friend class Seq; // calls methods below
template<class T_> friend class Rec; // calls methods below
bool _item() { return true ; }
bool _out() { levels--; return io->putChar(cbor::BreakByte); }
bool _item(QByteArray& k) { return (putInteger(cbor::TextStringType, k.size()) && io->write(k)); }
bool _item( ) { return true ; }
bool _out ( ) { levels--; return io->putChar(cbor::BreakByte ); }
private:
bool putInteger(char majorType, quint64 n)
{
......
......@@ -66,6 +66,7 @@ protected:
template<class T_> friend class Val; // calls methods below
bool _sequence() { *io << Sequence; return true; }
bool _record() { *io << Record ; return true; }
bool _null() { *io << Null ; return true; }
bool _bind(const char* s) { *io << s ; return true; }
......@@ -79,9 +80,11 @@ protected:
template<typename T> bool _bind(T t, typename std::enable_if<std::is_integral<T>::value && std:: is_signed<T>::value>::type* =nullptr) { *io << qlonglong(t); return true; }
template<class T_> friend class Seq; // calls methods below
template<class T_> friend class Rec; // calls methods below
bool _item() { *io << Item; return true; }
bool _out() { *io << Out ; return true; }
bool _item(QByteArray& k) { *io << Item; *io << k; return true; }
bool _item( ) { *io << Item; return true; }
bool _out( ) { *io << Out ; return true; }
private:
QDataStream* io;
};
......@@ -124,7 +127,8 @@ protected:
template<class T_> friend class Val; // calls methods below
bool _sequence() { if (get(Sequence)) { levels++; return true; } else return false; }
bool _null() { return get(Null); }
bool _record() { if (get(Record )) { levels++; return true; } else return false; }
bool _null() { return get(Null ); }
bool _bind(QByteArray& s) { io >> s; return true; }
// BindNative overloads
......@@ -138,9 +142,11 @@ protected:
bool _bind() { Q_ASSERT_X(false,Q_FUNC_INFO,"Cannot Read unknown type"); return false; }
template<class T_> friend class Seq; // calls methods below
template<class T_> friend class Rec; // calls methods below
bool _item() { return get(Item); }
bool _out() { if (get(Out)) { levels--; return true; } else return false; }
bool _item(QByteArray& k) { if (get(Item)) { io >> k ; return true; } else return false; }
bool _item( ) { if (get(Item)) { return true; } else return false; }
bool _out( ) { if (get(Out )) { levels--; return true; } else return false; }
private:
bool get(DataTag expected) {
......
......@@ -90,17 +90,18 @@ protected:
template<typename T> bool _bind(T& t, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr) { double d(t); return _bind(d); }
template<class T_> friend class Seq; // calls methods below
template<class T_> friend class Rec; // calls methods below
bool _item(const char* key) { levels.last().key=key ; return true; }
bool _item( ) { levels.last().key=nullptr; return true; }
bool _out() { auto level = levels.pop(); set(level.key ? QJsonValue(level.object) : QJsonValue(level.array)); return true; }
bool _item(QByteArray& k) { levels.last().key=k ; return true; }
bool _item( ) { levels.last().key=nullptr; return true; }
bool _out ( ) { auto level = levels.pop(); set(!level.key.isNull() ? QJsonValue(level.object) : QJsonValue(level.array)); return true; }
private:
void set(const QJsonValue& v) {
if (levels.isEmpty()) {
*value = v;
}
else {
if (levels.last().key)
if (!levels.last().key.isNull())
levels.last().object[levels.last().key]=v;
else
levels.last().array.append(v);
......@@ -108,7 +109,7 @@ private:
}
QJsonValue* value;
struct Step { const char* key; /* TODO union */ QJsonObject object; QJsonArray array; Step(const char* k=nullptr) : key(k) {} };
struct Step { QByteArray key; /* TODO union */ QJsonObject object; QJsonArray array; Step(const char* k=nullptr) : key(k) {} };
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) {}
......@@ -172,10 +173,11 @@ protected:
template<typename T> bool _bind(T& t, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr) { double d; if (_bind(d)) { t = d; return true; } return false; }
template<class T_> friend class Seq; // calls methods below
template<class T_> friend class Rec; // 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; }
bool _item( ) { steps.last().idx++ ; if (!(steps.last().item = current(1)->toArray (). at( steps.last().idx )).isUndefined()) { return true; } return false; }
bool _out() { steps.pop(); return true; }
bool _item(QByteArray& k) { steps.last().key=k; if (!(steps.last().item = current(1)->toObject().value(QString::fromUtf8(steps.last().key))).isUndefined()) { return true; } return false; }
bool _item( ) { steps.last().idx++; if (!(steps.last().item = current(1)->toArray (). at( steps.last().idx )).isUndefined()) { return true; } return false; }
bool _out ( ) { steps.pop(); return true; }
private:
const QJsonValue* current(unsigned outer=0) const { return steps.size()-outer <= 0 ? value : &(steps[steps.size()-outer-1].item); }
......@@ -220,12 +222,10 @@ protected:
template<class T_> friend class Val; // calls methods below
bool _sequence() { levels.push(Step{"","]"});
return io->write( "[" ); }
bool _null() { return io->write("null" ); }
bool _bind(const char* s) { io->write( "\"" );
escape ( s );
return io->write( "\"" ); }
bool _sequence() { levels.push(Step{"","]"}); return io->write( "[" ); }
bool _record() { levels.push(Step{"","}"}); return io->write( "{" ); }
bool _null() { return io->write("null" ); }
bool _bind(const char* s) { return putString( s ); }
// BindNative overloads
bool _bind( bool& n) { return io->write( n? "true" : "false" ); }
......@@ -239,14 +239,17 @@ protected:
return io->write(QByteArray::number( qlonglong(t))); }
template<class T_> friend class Seq; // calls methods below
template<class T_> friend class Rec; // calls methods below
bool _item() { auto r=io->write(levels.last().sep)==strlen(levels.last().sep); levels.last().sep = ","; return r; }
bool _out() { auto r=io->write(levels.pop() .end) ; return r; }
bool _item(QByteArray& k) { auto r=io->write(levels.last().sep)==strlen(levels.last().sep) && putString(k) && io->write(":"); levels.last().sep = ","; return r; }
bool _item( ) { auto r=io->write(levels.last().sep)==strlen(levels.last().sep) ; levels.last().sep = ","; return r; }
bool _out ( ) { return io->write(levels.pop() .end); }
private:
struct Step { const char* sep; const char* end; };
void escape(const char* utf8)
bool putString(const char* utf8)
{
io->putChar('"');
for (; utf8 && *utf8 ; utf8++)
{
/**/ if (*utf8 == '\\') { io->putChar('\\'); io->putChar('\\'); }
......@@ -269,6 +272,7 @@ private:
}
}
}
return io->putChar('"');
}
QIODevice* io;
......@@ -325,6 +329,12 @@ protected:
}
reportError(qBindExpectedSequence);
return false; }
bool _record() { if (get('{', "[{\"ntf-0123456789.")) {
levels.push(Step(-1,"}"));
return true;
}
reportError(qBindExpectedRecord);
return false; }
bool _null() { if (get('n', "[{\"ntf-0123456789.") &&
get('u', "[{\"" ) &&
get('l', "[{\"" ) &&
......@@ -388,19 +398,26 @@ protected:
}
template<class T_> friend class Seq; // calls methods below
template<class T_> friend class Rec; // calls methods below
// bool item(QByteArray& key) would require to cache key:value pairs coming out of order wrt to T items() calls
bool _item() { Step& level = levels.last();
if (-1 < level.index && !get(',',level.end)) {
return false;
}
level.index++;
return true; }
bool _out() { auto level = levels.pop();
while (get(',', level.end)) {
_bind();
}
return get(*level.end, "}"); }
// TODO cache key:value pairs coming out of order wrt to T items() calls
bool _item(QByteArray& k) { Step& level = levels.last();
if (-1 < level.index && !get(',',level.end)) {
return false;
}
level.index++;
return _bind(k) && get(':',level.end); }
bool _item( ) { Step& level = levels.last();
if (-1 < level.index && !get(',',level.end)) {
return false;
}
level.index++;
return true; }
bool _out ( ) { auto level = levels.pop();
while (get(',', level.end)) {
_bind();
}
return get(*level.end, "}"); }
private:
bool getNumber(double& d, qlonglong&i, bool& isNegative) {
......
......@@ -91,12 +91,15 @@ protected:
bool _null() { return true ; }
bool _sequence() { return io->write("["); }
bool _record() { return io->write("["); }
bool _bind(const char* s) { return io->write( s ); }
template<class T_> friend class Seq; // enables calling methods below
template<class T_> friend class Rec; // calls methods below
bool _item() { return io->write(" "); }
bool _out() { return io->write("]"); }
bool _item(QByteArray& k) { return io->write(" ") && io->write(k) && io->write(":"); }
bool _item( ) { return io->write(" ") ; }
bool _out ( ) { return io->write("]") ; }
private:
QIODevice* io = nullptr; // for QMovedWriter
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment