QBind.h 65.6 KB
Newer Older
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/****************************************************************************
 * **
 * ** 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
 * **     permission.
 * **
 * ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 * ** $QT_END_LICENSE$
 * **
 * ****************************************************************************/
#pragma once

#include <QtCore/qglobal.h>
#define Q_ENABLE_MOVE_DEFAULT(Class) \
44
45
46
    Class           (         ) noexcept = default; \
    Class           (Class&& o) noexcept = default; \
    Class& operator=(Class&& o) noexcept = default;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
47
#define Q_ENABLE_MOVE(Class, Statements) \
48
    Class           (         ) noexcept = default; \
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
49
50
51
    Class           (Class&& o) noexcept {                 Statements                 } \
    Class& operator=(Class&& o) noexcept { if (this!=&o) { Statements } return *this; }

52
#include "QData.h"
53

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
54
55
56
// //////////////////////////////////////////////////////////////////////////
// Standard error names

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
static QIdentifierLiteral qBindExpectedItem          ("ExpectedItem"          );
static QIdentifierLiteral qBindExpectedNull          ("ExpectedNull"          );
static QIdentifierLiteral qBindExpectedSequence      ("ExpectedSequence"      );
static QIdentifierLiteral qBindExpectedRecord        ("ExpectedRecord"        );
static QIdentifierLiteral qBindExpectedText          ("ExpectedText"          );
static QIdentifierLiteral qBindExpectedBytes         ("ExpectedBytes"         );
static QIdentifierLiteral qBindExpectedInteger       ("ExpectedInteger"       );
static QIdentifierLiteral qBindExpectedDecimal       ("ExpectedDecimal"       );
static QIdentifierLiteral qBindExpectedSmallerNumber ("ExpectedSmallerNumber" );
static QIdentifierLiteral qBindExpectedPositiveNumber("ExpectedPositiveNumber");
static QIdentifierLiteral qBindExpectedBoolean       ("ExpectedBoolean"       );
static QIdentifierLiteral qBindExpectedConstant      ("ExpectedConstant"      );

static QIdentifierLiteral qBindIgnoredItem           ("IgnoredItem"           );
static QIdentifierLiteral qBindIgnoredItemName       ("IgnoredItemName"       );
static QIdentifierLiteral qBindIgnoredCharacter      ("IgnoredCharacter"      );
static QIdentifierLiteral qBindIgnoredBytes          ("IgnoredBytes"          );
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
74
75
76
77

// //////////////////////////////////////////////////////////////////////////
// Standard meta data names

78
79
80
81
#include <vector>
#include <utility>
#include <algorithm>
#include <string>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
82
83
84

#include <QtCore/qstring.h>

85
// meta(QIdentifierLiteral name, QAsciiData value) is optional meta-data about current data to enable optimized processing (e.g. tag, , style, etc.) and/or transmission (QAbstractItemModel headers, CBOR tags, XML attribute, CSS, numpy.ndarray shapes, etc.)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
86

87
static const QIdentifierLiteral qmDataStreamVersion("qmDataStreamVersion"); //!< Allows IBind support of QDataStream
88

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
89
90
91
92
// N-dimensional data structures for which specific IWriter may have optimized implementations
// 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 can be implemented as table with columns below (record item names are always the same in a fixed order)
93
static const QIdentifierLiteral qmColumns ("columns" ); //!< comma-separated names
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
94
95

//! Sequence of nested non-empty sequences of definite sizes can be implemented as multi-dimensional array
96
static const QIdentifierLiteral qmSizes   ("sizes"   ); //!< comma-separated natural numbers
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
97
98

//! Sequence of records where item(qmNodes) contains children can be implemented as trees
99
static const QIdentifierLiteral qmChildren("children"); //!< name of a sequence of records with recursively the same name
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
100

101
//! Name of current data
102
static const QIdentifierLiteral qmName    ("name"    );
103

104
static const QIdentifierLiteral qmColor   ("color"   ); //!< comma-separated names
105

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
106
// //////////////////////////////////////////////////////////////////////////
107
// QBind<T,TResult>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
108
109

template<class T_> class Val;
110
111
112
template<class T_> class Cur;
struct IBind;
using Cursor = Cur<IBind>; //!< \remark It would be possible to implement Cursor without templates nor BindSupport by defining all IBind overloads
113

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
114
115
116
117
118
119
//! QBind is a class of template methods binding T parts with Val<TResult> obeying a simple generic data model, and returning the TResult
//! Each QBind<_,T> should provide bind(T&&) and bind(T&) overloads for convenience and may provide bind(const T&) for performance depending on T
//!
//! Its least specialized definition calls a T::bind(Val<TResult>&&) method that is more convenient to define than a QBind specialization
//! \see Person::bind() definition in main.cpp for an example
//!
120
121
//! \remark bind is not a template function to avoid conflicts with ADL and allow more precise class specialization
//! \remark Val<> is a move-only type passed by rvalue reference following http://scottmeyers.blogspot.com/2014/07/should-move-only-types-ever-be-passed.html
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
122
//! \remark bind is more explicit than operator() but could be changed to enable use as std::function
123
124
125
//! \remark 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
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
126
//!
127
template<typename T, class TResult=Cursor, typename TEnabledIf=void>
128
struct QBind {
129
130
131
    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<T,TResult>::bind(Val<TResult>,T&)
    static TResult bind(Val<TResult>&& value,const T&  t) { return const_cast<T&>(t).bind(std::move(value)); } // In case of error, define a T::bind(Val<TResult>) method or external QBind<T,TResult>::bind(Val<TResult>,const T&)
    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<T,TResult>::bind(Val<TResult>,T&&)
132
};
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
133

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
134
135
136
137
138
139
140
//!< A pair of T value reference and defaultValue to use instead of null()
template<typename T>
struct QBindDefault {
    T& value;
    const T& defaultValue;
};

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
141
142
143
144
145
146
// //////////////////////////////////////////////////////////////////////////
// Generic fluent interface for traversing structured values and processing them along the way:
// - serializing, deserializing
// - constructing generic in-memory data structures
// - etc...

147
148
#include <functional>

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
149
150
template<class T_> class Rec; //!< a Record   data structure defined below
template<class T_> class Seq; //!< a Sequence data structure defined below
151
template<class T_> class Val; //!< a choice of sequence(), record(), null(), or any value with at least a textual representation and possibly binary ones
152

153
// Custom bind support
154
155
156
157
158
159
#ifndef NO_COMPILER_RTTI_OR_EXCEPTIONS
template<typename T> using QBindFunction    =     Cursor (*)(T &,Val<Cursor>&&) ;
template<class   Ts> using QBindSeqFunction = Seq<Cursor>(*)(Ts&,Seq<Cursor>&&) ;
#endif
/**/     using QBindLambda    = std::function<    Cursor    (    Val<Cursor>&&)>;
/**/     using QBindSeqLambda = std::function<Seq<Cursor>   (    Seq<Cursor>&&)>;
160

161
template<class T_> class Val
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
162
163
164
165
{
    Q_DISABLE_COPY(Val)
public:
    Q_ENABLE_MOVE_DEFAULT(Val)
166
    explicit Val(T_&& out) noexcept { std::swap(outer, out); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
167
168
169

    using TResult = typename T_::TResult; //!< the result of the traversal

170
    operator       bool() noexcept { return outer.operator bool(); } //!< Drives QBind<T>::bind() traversal
171
    TResult* operator->() noexcept { return outer.operator   ->(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
172

173
    /**/             Val<T_> meta    (QIdentifierLiteral& n, QAsciiData& m) { outer->_meta(n,m); return std::move(*this); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
174

175
176
177
178
    /**/             Seq<T_> sequence(quint32* s=nullptr) { return outer->_sequence            (s)  ? Seq<T_>(std::move(outer)) : Seq<T_>(); }
    /**/             Rec<T_> record  (quint32* s=nullptr) { return outer->_record              (s)  ? Rec<T_>(std::move(outer)) : Rec<T_>(); }
    /**/                 T_  null    (                  ) { return outer->_null                ( )  ?         std::move(outer)  :     T_ (); }
    /**/                 T_  any     (                  ) { return outer->_any                 ( )  ?         std::move(outer)  :     T_ (); }
179
180
181
182
183
    /**/                 T_  bind    (     const char* u) { return bind(QUtf8DataView(u)); }
    /**/                 T_  bind    (   QUtf8DataView u) { return outer->_bind                (u)  ?         std::move(outer)  :     T_ (); }
    /**/                 T_  bind    (  QAsciiDataView a) { return outer->_bind                (a)  ?         std::move(outer)  :     T_ (); }
    /**/                 T_  bind    (   QLatin1String l) { return outer->_bind                (l)  ?         std::move(outer)  :     T_ (); }
    /**/                 T_  bind    (     QStringView u) { return outer->_bind                (u)  ?         std::move(outer)  :     T_ (); }
184
    template<typename T> T_  bind    (             T&& t) { return outer->_bind(std::forward<T>(t)) ?         std::move(outer)  :     T_ (); }
185
    template<typename T> T_  bind    (T& t, T&& defaultT) { return outer->_bind(QBindDefault<T>{t,defaultT})? std::move(outer)  :     T_ (); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
186

187
    // Custom bind support
188
189
    /**/                 T_  with    (      QBindLambda      customBind) { return customBind(   std::move(unsafeThis())) ? std::move(outer) : T_(); }
    template<typename T> T_  with    (T& t, QBindFunction<T> customBind) { return customBind(t, std::move(unsafeThis())) ? std::move(outer) : T_(); }
190

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
191
    // Literal metadata support
192
193
194
195
196
197
198
    Val<T_> meta(      QIdentifierLiteral&& n,       QAsciiData&& m) { QIdentifierLiteral nref=n; QAsciiData mref=m; return meta(nref, mref); }
    Val<T_> meta(const QIdentifierLiteral&  n,       QAsciiData&& m) { QIdentifierLiteral nref=n; QAsciiData mref=m; return meta(nref, mref); }
    Val<T_> meta(const QIdentifierLiteral&  n, const char*        m) { QIdentifierLiteral nref=n; QAsciiData mref=QAsciiData(m); return meta(nref, mref); }
    Val<T_> meta(      QIdentifierLiteral&& n, const QAsciiData&  m) { QIdentifierLiteral nref=n; QAsciiData mref=m; return meta(nref, mref); }
    Val<T_> meta(const char*                n, const char*        m) { return meta(QIdentifierLiteral(n),QAsciiData(m)); }
    Seq<T_> sequence(  quint32   s) { return sequence(&s); }
    Rec<T_> record  (  quint32   s) { return record  (&s); }
199

200
    // Shortcuts
201
    template<typename T> Seq<T_> operator<<(    T&& t) { return sequence().bind(std::forward<T>(t)); } // stream compatible
202
    /**/                 Rec<T_> record(const char* n) { return meta(qmName,QAsciiData(n)).record(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
203
private:
204
205
    Val<TResult> unsafeThis() noexcept { return Val<TResult>(outer->_unsafeCopy()); }

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
206
207
208
209
210
211
212
213
    T_ outer = T_(); //!< moved context of current traversal up to TResult that will point to the value itself (be it a QIODevice or QCborValue)
};

template<class T_> class Seq
{
    Q_DISABLE_COPY(Seq)
public:
    Q_ENABLE_MOVE_DEFAULT(Seq)
214
    explicit Seq(T_&& out) noexcept { std::swap(outer, out); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
215
216

    // T_ can be either a TResult or any combination of nested Seq or Rec like Rec<Seq<TResult>
217
    using TResult = typename T_::TResult;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
218

219
    operator       bool() noexcept { return outer.operator bool(); } //!< Drives QBind<T>::bind() traversal
220
    TResult* operator->() noexcept { return outer.operator   ->(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
221
222
223
    TResult      result() { return operator TResult(); }
    operator    TResult() { return out(); /* calls T_::operator TResult() if T_ != TResult */ }

224
225
    /**/    T_    out() { return outer-> _out() ?              std::move(outer)  :         T_  (); }
    Val<Seq<T_>> item() { return outer->_item() ? Val<Seq<T_>>(std::move(*this)) : Val<Seq<T_>>(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
226
227

    // Shortcuts
228
229
230
231
232
233
234
235
236
    /**/             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                ( ) ; }
    /**/                 Seq<T_>  any     (                  ) { return item().any                 ( ) ; }
    /**/                 Seq<T_>  bind    (     const char* u) { return bind(QUtf8DataView(u)); }
    /**/                 Seq<T_>  bind    (   QUtf8DataView u) { return item().bind                (u) ; }
    /**/                 Seq<T_>  bind    (  QAsciiDataView a) { return item().bind                (a) ; }
    /**/                 Seq<T_>  bind    (   QLatin1String l) { return item().bind                (l) ; }
    /**/                 Seq<T_>  bind    (     QStringView u) { return item().bind                (u) ; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
237
    template<typename T> Seq<T_>  bind    (             T&& t) { return item().bind(std::forward<T>(t)); }
238
    template<typename T> Rec<T_>  bind    (T& t, T&& defaultT) { return item().bind(t,std::forward<T>(defaultT)); }
239

240
    // Custom bind support
241
242
    /**/                 Seq<T_>  with    (        QBindSeqLambda       customBind) { return bool(customBind(    std::move(unsafeThis()))) ? std::move(*this) : Seq<T_>(); }
    template<class   Ts> Seq<T_>  with    (Ts& ts, QBindSeqFunction<Ts> customBind) { return bool(customBind(ts, std::move(unsafeThis()))) ? std::move(*this) : Seq<T_>(); }
243
244
    template<class   Ts> Seq<T_>  forEach (Ts& ts, TResult(*)(      typename Ts::value_type&, Val<TResult>&&)
                                                 , bool   (*)(const typename Ts::value_type&) = [](const typename Ts::value_type&) { return true; });
245

246
247
    // Stream compatible shortcut
    template<typename T> Seq<T_> operator<<(T&& t) { return item().bind(std::forward<T>(t)); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
248
private:
249
    template<typename T, class TResult, typename TEnabledIf> friend struct QBind;
250
    Val<TResult> unsafeItem() noexcept { return outer->_item() ? Val<TResult>(outer->_unsafeCopy()) : Val<TResult>(); }
251
    Seq<TResult> unsafeThis() noexcept { return                  Seq<TResult>(outer->_unsafeCopy())                 ; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
252
253
254
255
256
257
258
259
260

    T_ outer = T_();
};

template<class T_> class Rec
{
    Q_DISABLE_COPY(Rec)
public:
    Q_ENABLE_MOVE_DEFAULT(Rec)
261
    explicit Rec(T_&& out) noexcept { std::swap(outer, out); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
262
263

    // T_ can be either a TResult or any combination of nested Seq or Rec like Seq<Rec<TResult>
264
    using TResult = typename T_::TResult;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
265

266
    operator       bool() noexcept { return outer.operator bool(); } //!< Drives QBind<T>::bind() traversal
267
    TResult* operator->() noexcept { return outer.operator   ->(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
268
269
270
    TResult      result() { return operator TResult(); }
    operator    TResult() { return out(); /* calls T_::operator TResult() if T_ != TResult */ }

271
272
273
    /**/    T_   out (              ) { return outer->_out (                     ) ?              std::move(outer)  :         T_  (); }
    Val<Rec<T_>> item(QIdentifier& n) { return outer->_item(                   n ) ? Val<Rec<T_>>(std::move(*this)) : Val<Rec<T_>>(); }
    Val<Rec<T_>> item(const char*  n) { return outer->_item(QIdentifierLiteral(n)) ? Val<Rec<T_>>(std::move(*this)) : Val<Rec<T_>>(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
274
275

    // Shortcuts
276
277
278
279
280
281
282
283
284
285
    /**/             Seq<Rec<T_>> sequence(const char* n, quint32* s=nullptr) { return item(n).sequence            (s) ; }
    /**/             Rec<Rec<T_>> record  (const char* n, quint32* s=nullptr) { return item(n).record              (s) ; }
    /**/                 Rec<T_>  null    (const char* n                    ) { return item(n).null                ( ) ; }
    /**/                 Rec<T_>  bind    (const char* n,      const char* u) { return item(n).bind(  QUtf8DataView(u)); }
    /**/                 Rec<T_>  bind    (const char* n,    QUtf8DataView u) { return item(n).bind                (u) ; }
    /**/                 Rec<T_>  bind    (const char* n,   QAsciiDataView a) { return item(n).bind                (a) ; }
    /**/                 Rec<T_>  bind    (const char* n,    QLatin1String l) { return item(n).bind                (l) ; }
    /**/                 Rec<T_>  bind    (const char* n,      QStringView u) { return item(n).bind                (u) ; }
    template<typename T> Rec<T_>  bind    (const char* n,              T&& t) { return item(n).bind(std::forward<T>(t)); }
    template<typename T> Rec<T_>  bind    (const char* n, T& t, T&& defaultT) { return item(n).bind(t,std::forward<T>(defaultT)); }
286
287

    // Custom bind support
288
289
    /**/                 Rec<T_>  with    (const char* n,       QBindLambda      customBind) { return item(n).with    (   customBind); }
    template<typename T> Rec<T_>  with    (const char* n, T& t, QBindFunction<T> customBind) { return item(n).bind    (t, customBind); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
290
private:
291
    template<typename T, class TResult, typename TEnabledIf> friend struct QBind;
292
    Val<TResult> unsafeItem(QIdentifier& n) noexcept { return outer->_item(n) ? Val<TResult>(outer._unsafeCopy()) : Val<TResult>(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
293
294
295
296

    T_ outer = T_();
};

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
297
298
299
300
301
302
303
// //////////////////////////////////////////////////////////////////////////
// Default TResult implementations

enum BindMode { Invalid=0, Read=1, Write=2 }; //!< Specifies QBind::bind traversal and processing (the design would support other BindMode like Append or Diff)

struct BindGeneric {};
struct BindNative  {};
304
template<typename T, class TImpl=IBind, typename TEnabledIf=void> struct BindSupport : BindGeneric {}; //!< Specifies whether Val calls TResult::_bind or QBind<T,TResult>::bind
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
305
306
307
308
309
310
311
312
313
314
315
316
317

#include <type_traits>
template<class T> using RemoveCvRef = typename std::remove_cv<typename std::remove_reference<T>::type>::type;

//! A Cur(sor) is a move-only, non-owning pointer to TImpl that can be used as QBind's TResult
//! It allows to easily add QBind support to existing Reader/Writers by adding at least one value() method returning a Cursor(this).value()
//! \see TextWriter in main.cpp
//!
//! \remark Cur allows using the fluent interface without checking intermediate status by setting impl=nullptr on error
//! and subsequently bypassing impl calls. Errors can result from:
//! - using the same intermediate Val/Seq/Rec twice (a programmer error), or
//! - runtime impl errors like trying to bind a _sequence() whereas the data read matches a _record()
//!
318
template<class TImpl> //!< Processing associated to the traversal
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
319
320
321
322
323
324
325
326
class Cur
{
    Q_DISABLE_COPY(Cur)
public:
    Q_ENABLE_MOVE(Cur, std::swap(impl, o.impl); )

    using TResult = Cur;

327
    explicit Cur(TImpl* i) : impl(i) { Q_ASSERT(impl); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
328

329
    BindMode mode() const noexcept { return impl ? impl->mode() : BindMode::Invalid; }
330

331
    operator    bool() noexcept { return impl && impl->_isOk(); } //!< Drives QBind<T>::bind() traversal
332
    Cur* operator ->() noexcept { return this; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
333

334
    Val<Cur> value() noexcept { return Val<Cur>(std::move(*this)); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
335

336
337
338
    void setChoice  (bool               c) { if (impl) impl->_setChoice  (c); }
    void reportError(QIdentifierLiteral e) { if (impl) impl->_reportError(e); }
    void reportError(const char*        e) { reportError(QIdentifierLiteral(e)); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
339
340
341
342
343
protected:
    template<class T_> friend class Val; // enables calling methods below
    template<class T_> friend class Seq;
    template<class T_> friend class Rec;

344
345
346
347
348
349
350
351
352
353
    void _meta    (QIdentifierLiteral& n,
                   QAsciiData&         m) { if    (Q_LIKELY(impl) )  impl->_meta  (n,m); } //!< idempotent (can be called by more than one code)
    bool _sequence(   quint32* s=nullptr) { return Q_LIKELY(impl) && impl->_sequence(s); }
    bool _record  (   quint32* s=nullptr) { return Q_LIKELY(impl) && impl->_record  (s); }
    bool _null    (                     ) { return Q_LIKELY(impl) && impl->_null    ( ); }
    bool _any     (                     ) { return Q_LIKELY(impl) && impl->_any     ( ); }
    bool _bind    (      QUtf8DataView u) { return Q_LIKELY(impl) && impl->_bind    (u); }
    bool _bind    (     QAsciiDataView a) { return Q_LIKELY(impl) && impl->_bind    (a); }
    bool _bind    (      QLatin1String l) { return Q_LIKELY(impl) && impl->_bind    (l); }
    bool _bind    (        QStringView u) { return Q_LIKELY(impl) && impl->_bind    (u); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
354

355
    template<typename T> bool _bind(             T&& t) { return _bind(BindSupport<RemoveCvRef<T>,TImpl>()              ,std::forward<T>(t)); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
356
private:
357
    template<typename T> bool _bind(BindNative , T&& t) { return Q_LIKELY(impl) &&   impl->_bind(                        std::forward<T>(t)); }
358
    template<typename T> bool _bind(BindGeneric, T&& t) { return QBind<RemoveCvRef<T>,Cur>::bind(Val<Cur>(_unsafeCopy()),std::forward<T>(t)); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
359

360
361
362
363
    bool _item(QIdentifierLiteral n) { return Q_LIKELY(impl) && impl->_item(n); }
    bool _item(QIdentifier&       n) { return Q_LIKELY(impl) && impl->_item(n); }
    bool _item(                    ) { return Q_LIKELY(impl) && impl->_item( ); }
    bool _out (                    ) { return Q_LIKELY(impl) && impl->_out ( ); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
364
private:
365
    Cur _unsafeCopy() noexcept { return impl ? Cur(impl) : Cur(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
366
367
368
369

    TImpl* impl = nullptr;
};

370
template<class TResult=Cursor>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
371
372
373
374
375
376
377
378
379
380
class QScopedChoice //!< for accurate error reporting
{
    Q_DISABLE_COPY(QScopedChoice)
public:
    QScopedChoice(TResult& t) : t(t) { if(t) t->setChoice(true ); }
   ~QScopedChoice(          )        { if(t) t->setChoice(false); }
private:
    TResult& t;
};

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
381
// //////////////////////////////////////////////////////////////////////////
382
// Base Cur<TImpl> implementations for Read and Write BindMode
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
383

384
#include <QtCore/qvariant.h>
385
386
387
388
//#include <QtCore/qfloat16.h>
//#include <QtCore/qdatetime.h>
//#include <QtCore/quuid.h>

389
//! Interface for Cur's TImpl with a fixed subset of BindNative types and just a few optional methods
390
391
392
struct IBind {
    virtual ~IBind() = default;

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
393
    virtual BindMode mode() const = 0; //!< \remark a static constexpr BindMode Mode did not exhibit noticeable performance improvements and may trigger twice more code generation for Read/Write independant QBind like Person::bind
394
395
396
397
398
399
400

    virtual bool _isOk() = 0; //!< Current operation status

    virtual bool _sequence(quint32* size=nullptr) = 0;
    virtual bool _record  (quint32* size=nullptr) = 0;
    virtual bool _null    (                     ) = 0;

401
402
403
404
    virtual bool _item(                    ) = 0;
    virtual bool _item(QIdentifier&       n) = 0;
    virtual bool _item(QIdentifierLiteral n) = 0;
    virtual bool _out (                    ) = 0; //!< End of sequence or record
405

406
407
408
409
410
411
412
    virtual bool _bind( QUtf8DataView u) = 0;
    virtual bool _bind(   const char* u) = 0;
    virtual bool _bind(QAsciiDataView a) = 0;
    virtual bool _bind( QLatin1String l) = 0;
    virtual bool _bind(   QStringView s) = 0;

    virtual bool _bind(        QUtf8Data&  r) = 0;
413
414
    virtual bool _bind(          QString&  r) = 0;
    virtual bool _bind(             bool&  r) = 0;
415
416
417
418
419
420
421
422
423
424
    virtual bool _bind(            qint8&  r) = 0; //!< \warning Must return false instead of losing sign or digit
    virtual bool _bind(           quint8&  r) = 0; //!< \warning Must return false instead of losing sign or digit
    virtual bool _bind(           qint16&  r) = 0; //!< \warning Must return false instead of losing sign or digit
    virtual bool _bind(          quint16&  r) = 0; //!< \warning Must return false instead of losing sign or digit
    virtual bool _bind(           qint32&  r) = 0; //!< \warning Must return false instead of losing sign or digit
    virtual bool _bind(          quint32&  r) = 0; //!< \warning Must return false instead of losing sign or digit
    virtual bool _bind(           qint64&  r) = 0; //!< \warning Must return false instead of losing sign or digit
    virtual bool _bind(          quint64&  r) = 0; //!< \warning Must return false instead of losing sign or digit
    virtual bool _bind(            float&  r) = 0; //!< \warning Must return false instead of losing sign or digit
    virtual bool _bind(           double&  r) = 0; //!< \warning Must return false instead of losing sign or digit
425
    virtual bool _bind(       QByteArray&  r) = 0;
426
    virtual bool _bind(         QVariant&  r) = 0;
427
428
429

    // TODO QChar, QDateTime, QDate, QTime, QUuid (text or numerical)

430
431
    // Overloads for const& and && T

432
    virtual bool _bind(const   QUtf8Data&  r) = 0;
433
434
435
436
437
438
439
440
441
442
443
444
445
    virtual bool _bind(const     QString&  r) = 0;
    virtual bool _bind(const        bool&  r) = 0;
    virtual bool _bind(const       qint8&  r) = 0;
    virtual bool _bind(const      quint8&  r) = 0;
    virtual bool _bind(const      qint16&  r) = 0;
    virtual bool _bind(const     quint16&  r) = 0;
    virtual bool _bind(const      qint32&  r) = 0;
    virtual bool _bind(const     quint32&  r) = 0;
    virtual bool _bind(const      qint64&  r) = 0;
    virtual bool _bind(const     quint64&  r) = 0;
    virtual bool _bind(const       float&  r) = 0;
    virtual bool _bind(const      double&  r) = 0;
    virtual bool _bind(const  QByteArray&  r) = 0;
446
    virtual bool _bind(const    QVariant&  r) = 0;
447

448
    virtual bool _bind(        QUtf8Data&& r) = 0;
449
450
451
452
453
454
455
456
457
458
459
460
461
    virtual bool _bind(          QString&& r) = 0;
    virtual bool _bind(             bool&& r) = 0;
    virtual bool _bind(            qint8&& r) = 0;
    virtual bool _bind(           quint8&& r) = 0;
    virtual bool _bind(           qint16&& r) = 0;
    virtual bool _bind(          quint16&& r) = 0;
    virtual bool _bind(           qint32&& r) = 0;
    virtual bool _bind(          quint32&& r) = 0;
    virtual bool _bind(           qint64&& r) = 0;
    virtual bool _bind(          quint64&& r) = 0;
    virtual bool _bind(            float&& r) = 0;
    virtual bool _bind(           double&& r) = 0;
    virtual bool _bind(       QByteArray&& r) = 0;
462
    virtual bool _bind(         QVariant&& r) = 0;
463

464
465
    virtual bool _any() { return _null(); }

466
467
    //! \warning meta() is ignored by default and subject to various interpretation, so users should define or adopt existing meta data standards like XSD for sake of interoperability
    virtual void _meta(QIdentifierLiteral&, QAsciiData&) {}
468
469

    virtual void _setChoice(bool) {}
470
    virtual void _reportError(QIdentifierLiteral) {}
471
};
472

473
template<> struct BindSupport<  QUtf8Data> : BindNative {};
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
template<> struct BindSupport<    QString> : BindNative {};
//template<> struct BindSupport<      QChar> : BindNative {};
template<> struct BindSupport<       bool> : BindNative {};
template<> struct BindSupport<      qint8> : BindNative {};
template<> struct BindSupport<     quint8> : BindNative {};
template<> struct BindSupport<     qint16> : BindNative {};
template<> struct BindSupport<    quint16> : BindNative {};
template<> struct BindSupport<     qint32> : BindNative {};
template<> struct BindSupport<    quint32> : BindNative {};
template<> struct BindSupport<     qint64> : BindNative {};
template<> struct BindSupport<    quint64> : BindNative {};
//template<> struct BindSupport<   qfloat16> : BindNative {};
template<> struct BindSupport<      float> : BindNative {};
template<> struct BindSupport<     double> : BindNative {};
//template<> struct BindSupport<      QDate> : BindNative {};
//template<> struct BindSupport<  QDateTime> : BindNative {};
//template<> struct BindSupport<      QTime> : BindNative {};
//template<> struct BindSupport<      QUuid> : BindNative {};
template<> struct BindSupport< QByteArray> : BindNative {};
493
template<> struct BindSupport<   QVariant> : BindNative {};
494

495
static char *qulltoa2(char *p, qulonglong n, int base=10) //!< Reproduced here for benchmark purposes only
496
497
498
499
500
501
502
503
504
505
{
    *--p = '\0';
    const char b = 'a' - 10;
    do {
        const int c = n % base;
        n /= base;
        *--p = c + (c < 10 ? '0' : b);
    } while (n);
    return p;
}
506
static char * qlltoa2(char *p,  qlonglong n, int base=10) //!< Reproduced here for benchmark purposes only
507
508
509
510
511
512
513
514
515
516
{
    if (n < 0) {
        p = qulltoa2(p, qulonglong(-(1 + n)) + 1, base);
        *--p = '-';
    } else {
        p = qulltoa2(p, qulonglong(n), base);
    }
    return p;
}

517
518
#include <QtCore/qdatastream.h>

519
//! Base IBind implementation with BindMode::Write
520
struct IWriter : public IBind
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
521
{
522
    virtual ~IWriter() noexcept = default;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
523

524
    virtual BindMode mode() const noexcept { return BindMode::Write; }
525

526
    virtual bool _isOk() noexcept { return true; } //!< Write status ignored by default (no need to test intermediate status to choose between _sequence(), etc.
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
527

528
529
530
    virtual bool _item(                    ) = 0;
    virtual bool _item(QIdentifier&       n) = 0;
    virtual bool _item(QIdentifierLiteral n) { QIdentifier id(n); return _item(id); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
531
532
533

    //! 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)
534
    virtual bool _out () { return true; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
535

536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
    virtual bool _bind( QUtf8DataView u) = 0;
    virtual bool _bind(   const char* u) { return _bind(QUtf8DataView(u)); } // required to match better than QString overloads
    virtual bool _bind(QAsciiDataView a) { return _bind(QUtf8DataView(a.data(), a.size())); }
    virtual bool _bind( QLatin1String l) { return _bind(QString(l)); }
    virtual bool _bind(   QStringView s) { return _bind(QUtf8DataView(s.toUtf8())); }

    virtual bool _bind(   QUtf8Data&& s) { return _bind(QUtf8DataView(s)); }
    virtual bool _bind(     QString&& s) { return _bind(QStringView(s.data(), s.size())); }
    virtual bool _bind(        bool&& s) { return _bind(s ? QAsciiDataView("true") : QAsciiDataView("false")); }
    virtual bool _bind(       qint8&& n) { return _bind( qint64(n)); }
    virtual bool _bind(      quint8&& n) { return _bind(quint64(n)); }
    virtual bool _bind(      qint16&& n) { return _bind( qint64(n)); }
    virtual bool _bind(     quint16&& n) { return _bind(quint64(n)); }
    virtual bool _bind(      qint32&& n) { return _bind( qint64(n)); }
    virtual bool _bind(     quint32&& n) { return _bind(quint64(n)); }
    virtual bool _bind(      qint64&& n) { const int s=66; char c[s]; char* start= qlltoa2(c+s, n); return _bind(QAsciiDataView(start,(c+s)-start-1)); }
    virtual bool _bind(     quint64&& n) { const int s=66; char c[s]; char* start=qulltoa2(c+s, n); return _bind(QAsciiDataView(start,(c+s)-start-1)); }
    virtual bool _bind(       float&& n) { static QByteArray s; s.setNum(double(n),'g',std::numeric_limits<   float>::max_digits10); return _bind(QAsciiDataView(s.constData(),s.size())); } // with specific precision
    virtual bool _bind(      double&& n) { static QByteArray s; s.setNum(       n ,'g',std::numeric_limits<  double>::max_digits10); return _bind(QAsciiDataView(s.constData(),s.size())); } // with specific precision
    virtual bool _bind(  QByteArray&& s) { QByteArray h; h.reserve(s.size()*2+2+1); h.append("0x").append(s.toHex())               ; return _bind(QAsciiDataView(h.constData(),h.size())); }
    virtual bool _bind(  QVariant&& src) {
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
        if (src.canConvert<QVariantList>()) {
            QSequentialIterable ts = src.value<QSequentialIterable>();
            quint32 size=quint32(ts.size());
            if (!_sequence(&size)) {
                return false;
            }
            for (QVariant t : ts) {
                if (!_item() || !_bind(QVariant(t))) {
                    return false;
                }
            }
            return _out();
        }
        if (src.canConvert<QVariantMap >() ) { // TODO _meta({{qmColumns,"key,value"}}) _sequence() ...
            QAssociativeIterable ts = src.value<QAssociativeIterable>();
            quint32 size=quint32(ts.size());
            if (!_record(&size)) {
                return false;
            }
            QAssociativeIterable::const_iterator i = ts.begin();
            const QAssociativeIterable::const_iterator end = ts.end();
            for ( ; i != end; ++i) {
579
580
                QIdentifier id(i.key().toString().toLatin1());
                if (!_item(id) || !_bind(QVariant(*i))) {
581
582
583
584
585
                    return false;
                }
            }
            return _out();
        }
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
        if (src.isNull()                        ) return _null();
        if (src.userType() // TODO src.type if QUtf8Data becomes a Qt type
                      ==qMetaTypeId<QUtf8Data>()) return _bind(src.value<  QUtf8Data>());
        if (src.type()==QVariant::String        ) return _bind(src.value<    QString>());
        if (src.type()==QVariant::Char          ) return _bind(src.          toString());
        if (src.type()==QVariant::Bool          ) return _bind(src.value<       bool>());
        if (src.type()==QVariant::LongLong      ) return _bind(src.value<  qlonglong>()); // or  qint64 to be sure to call the good _bind()?
        if (src.type()==QVariant::ULongLong     ) return _bind(src.value< qulonglong>()); // or quint64 to be sure to call the good _bind()?
        if (src.type()==QMetaType::ULong        ) return _bind(src.value<    quint32>());
        if (src.type()==QMetaType::Long         ) return _bind(src.value<     qint32>());
        if (src.type()==QMetaType::Short        ) return _bind(src.value<      short>());
        if (src.type()==QMetaType::Char         ) return _bind(src.value<       char>());
        if (src.type()==QMetaType::UShort       ) return _bind(src.value<     ushort>());
        if (src.type()==QMetaType::UChar        ) return _bind(src.value<      uchar>());
        if (src.type()==QMetaType::SChar        ) return _bind(src.value<signed char>());
        if (src.type()==QVariant::UInt          ) return _bind(src.value<       uint>());
        if (src.type()==QVariant::Int           ) return _bind(src.value<        int>());
        if (src.type()==QVariant::Double        ) return _bind(src.value<     double>());
        if (src.type()==QMetaType::Float        ) return _bind(src.value<      float>());
        // See QT_FOR_EACH_STATIC_PRIMITIVE_TYPE in qmetatype.h
606
607
608

        // QDataStream format is the only Qt data format that is read-write and easily extensible by users
        // It is binary and includes src.typeName() for QMetaType::User types
609
        // QByteArray will be encoded into a QVariant to avoid ambiguities when decoding bytes
610
611
612
613
614
615
616
617
618

        QByteArray binaryVariant; QDataStream s(&binaryVariant, QIODevice::WriteOnly);
        s << src;
        if (s.status()==QDataStream::Ok) {
            return _bind(binaryVariant);
        }

        return _null();
    }
619

620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
    virtual bool _bind(       QUtf8Data& r) {  QUtf8Data copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(         QString& r) {    QString copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(            bool& r) {       bool copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(           qint8& r) {      qint8 copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(          quint8& r) {     quint8 copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(          qint16& r) {     qint16 copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(         quint16& r) {    quint16 copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(          qint32& r) {     qint32 copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(         quint32& r) {    quint32 copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(          qint64& r) {     qint64 copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(         quint64& r) {    quint64 copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(           float& r) {      float copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(          double& r) {     double copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(      QByteArray& r) { QByteArray copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(        QVariant& r) {   QVariant copy(r); return _bind(std::move(copy)); }

    virtual bool _bind(const  QUtf8Data& r) {  QUtf8Data copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(const    QString& r) {    QString copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(const       bool& r) {       bool copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(const      qint8& r) {      qint8 copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(const     quint8& r) {     quint8 copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(const     qint16& r) {     qint16 copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(const    quint16& r) {    quint16 copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(const     qint32& r) {     qint32 copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(const    quint32& r) {    quint32 copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(const     qint64& r) {     qint64 copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(const    quint64& r) {    quint64 copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(const      float& r) {      float copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(const     double& r) {     double copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(const QByteArray& r) { QByteArray copy(r); return _bind(std::move(copy)); }
    virtual bool _bind(const   QVariant& r) {   QVariant copy(r); return _bind(std::move(copy)); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
651
652
};

653
//! Base IBind implementations with BindMode::Read
654
struct IReader : public IBind
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
655
{
656
    virtual ~IReader() noexcept = default;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
657

658
    virtual BindMode mode() const noexcept { return BindMode::Read; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
659

660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
    virtual bool _item(                    ) = 0;
    virtual bool _item(QIdentifier&       n) = 0;
    virtual bool _item(QIdentifierLiteral n) { QIdentifier id; return _item(id) && id.utf8()==n.utf8(); }

    virtual bool _bind(   const char* u) { return _bind(QUtf8DataView(u)); }
    virtual bool _bind( QUtf8DataView u) { QUtf8Data r; if (_bind(r) && r.utf8()==u.data()) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(QAsciiDataView a) { QUtf8Data r; if (_bind(r) && r.utf8()==a.data()) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind( QLatin1String l) { QString   r; if (_bind(r) && r==l              ) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(   QStringView s) { QString   r; if (_bind(r) && r==s              ) return true; _reportError(qBindExpectedConstant); return false; }

    virtual bool _bind(  QUtf8Data& s) = 0;
    virtual bool _bind(    QString& s) { QUtf8Data u; if (!_bind(u)) return false; s=QString::fromUtf8(u.utf8()); return true; }
    virtual bool _bind(       bool& b) { QUtf8Data u; if (!_bind(u)) return false; if (u.utf8().compare("true" , Qt::CaseInsensitive)==0) { b=true ; } else
                                                                                   if (u.utf8().compare("false", Qt::CaseInsensitive)==0) { b=false; } else                 { _reportError(qBindExpectedBoolean      ); return false; }               return true; }
    virtual bool _bind(      qint8& n) {    qint64 l; if (!_bind(l)) return false; if (l<std::numeric_limits<  qint8>::min() || std::numeric_limits<  qint8>::max()<l)      { _reportError(qBindExpectedSmallerNumber); return false; } n=  qint8(l); return true; }
    virtual bool _bind(     quint8& n) {   quint64 l; if (!_bind(l)) return false; if (                                         std::numeric_limits< quint8>::max()<l)      { _reportError(qBindExpectedSmallerNumber); return false; } n= quint8(l); return true; }
    virtual bool _bind(     qint16& n) {    qint64 l; if (!_bind(l)) return false; if (l<std::numeric_limits< qint16>::min() || std::numeric_limits< qint16>::max()<l)      { _reportError(qBindExpectedSmallerNumber); return false; } n= qint16(l); return true; }
    virtual bool _bind(    quint16& n) {   quint64 l; if (!_bind(l)) return false; if (                                         std::numeric_limits<quint16>::max()<l)      { _reportError(qBindExpectedSmallerNumber); return false; } n=quint16(l); return true; }
    virtual bool _bind(     qint32& n) {    qint64 l; if (!_bind(l)) return false; if (l<std::numeric_limits< qint32>::min() || std::numeric_limits< qint32>::max()<l)      { _reportError(qBindExpectedSmallerNumber); return false; } n= qint32(l); return true; }
    virtual bool _bind(    quint32& n) {   quint64 l; if (!_bind(l)) return false; if (                                         std::numeric_limits<quint32>::max()<l)      { _reportError(qBindExpectedSmallerNumber); return false; } n=quint32(l); return true; }
    virtual bool _bind(     qint64& n) { QUtf8Data s; if (!_bind(s)) return false; bool isOk=false;  qint64 v; v=s.utf8().toLongLong (&isOk); if (isOk) { n=v; return true; } _reportError(qBindExpectedDecimal      ); return false; }
    virtual bool _bind(    quint64& n) { QUtf8Data s; if (!_bind(s)) return false; bool isOk=false; quint64 v; v=s.utf8().toULongLong(&isOk); if (isOk) { n=v; return true; } _reportError(qBindExpectedDecimal      ); return false; }
    virtual bool _bind(      float& n) { QUtf8Data s; if (!_bind(s)) return false; bool isOk=false;   float v; v=s.utf8().toFloat    (&isOk); if (isOk) { n=v; return true; } _reportError(qBindExpectedDecimal      ); return false; }
    virtual bool _bind(     double& n) { QUtf8Data s; if (!_bind(s)) return false; bool isOk=false;  double v; v=s.utf8().toDouble   (&isOk); if (isOk) { n=v; return true; } _reportError(qBindExpectedDecimal      ); return false; }
    virtual bool _bind( QByteArray& b) { QUtf8Data s; if (!_bind(s)) return false; return toByteArray(b, s); }
685
686
687
    virtual bool _bind( QVariant& dst) {
        _setChoice(true);

688
        quint32 size=0; QIdentifier key; QVariant item;
689
690
        if (_sequence(&size)) { QVariantList l; while (_item(   )) { l.append(              _bind(item) ? item : QVariant()); } dst = l; return _out(); }
        if (_record  (&size)) { QVariantMap  l; while (_item(key)) { l.insert(key.latin1(), _bind(item) ? item : QVariant()); } dst = l; return _out(); }
691
692
693
694
695
        bool        b; if (_bind( b)) { dst = QVariant(b); return true; }
        quint64     u; if (_bind( u)) { dst = QVariant(u); return true; }
        qint64      l; if (_bind( l)) { dst = QVariant(l); return true; }
        double      d; if (_bind( d)) { dst = QVariant(d); return true; }
        QByteArray ba; if (_bind(ba)) { toVariant(dst,ba); return true; }
696
        QUtf8Data  u8; if (_bind(u8)) { dst = QVariant::fromValue(u8); return true; }
697
        QString     t; if (_bind( t)) { dst = QVariant(t); return true; }
698
        _setChoice(false);
699
        if (!_null()) _reportError(QIdentifierLiteral("ExpectedOneOfBooleanDecimalBytesTextSequenceRecordNull"));
700
701
        /**/                          dst = QVariant( ); return true;
    }
702

703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
    virtual bool _bind(const  QUtf8Data&  k) {  QUtf8Data r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(const    QString&  k) {    QString r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(const       bool&  k) {       bool r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(const      qint8&  k) {      qint8 r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(const     quint8&  k) {     quint8 r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(const     qint16&  k) {     qint16 r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(const    quint16&  k) {    quint16 r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(const     qint32&  k) {     qint32 r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(const    quint32&  k) {    quint32 r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(const     qint64&  k) {     qint64 r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(const    quint64&  k) {    quint64 r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(const      float&  k) {      float r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; } // TODO silent warning
    virtual bool _bind(const     double&  k) {     double r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; } // TODO silent warning
    virtual bool _bind(const QByteArray&  k) { QByteArray r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(const   QVariant&  k) {   QVariant r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }

    virtual bool _bind(       QUtf8Data&& k) {  QUtf8Data r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(         QString&& k) {    QString r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(            bool&& k) {       bool r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(           qint8&& k) {      qint8 r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(          quint8&& k) {     quint8 r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(          qint16&& k) {     qint16 r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(         quint16&& k) {    quint16 r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(          qint32&& k) {     qint32 r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(         quint32&& k) {    quint32 r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(          qint64&& k) {     qint64 r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(         quint64&& k) {    quint64 r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(           float&& k) {      float r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; } // TODO silent warning
    virtual bool _bind(          double&& k) {     double r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; } // TODO silent warning
    virtual bool _bind(      QByteArray&& k) { QByteArray r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
    virtual bool _bind(        QVariant&& k) {   QVariant r; if (_bind(r) && k==r) return true; _reportError(qBindExpectedConstant); return false; }
734

735
736
737
738
739
740
    virtual bool _any() { _setChoice(true);
                          if (  (_sequence() && _out())
                              ||(_record  () && _out())
                              || _null    ()          ) {
                              return true;
                          };
741
742
743
744
                          bool    b; // usually easier to check than types below
                          double  d; // matches most used numbers more easily than types below
                          qint64  i; // matches most used integrals more easily than types below
                          quint64 u;
745
                          QVariant v;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
746
                          QByteArray bytes;
747
                          if (_bind(b) || _bind(d) || _bind(i) || _bind(u) || _bind(v) || _bind(bytes)) {
748
749
                              return true;
                          }
750
                          _setChoice(false);
751
                          QString s; // matches any type with usually less encoding work than QUtf8Data (which only captures efficiently utf-8 whereas QChar easily captures Latin1 in addition to ASCII)
752
753
                          return _bind(s);
                        }
754
protected:
755
756
    bool toByteArray(QByteArray& b, QUtf8Data s) {
        const QByteArray& bytes = s.utf8();
757
        if (!bytes.startsWith("0x") || s.size()%2==1) return false;
758
759
760
        b.reserve((s.size()-2)/2);
        auto toHex = [](char a) -> char { return '0'<=a && a<='9' ? a-'0' : 'A'<=a && a<='F' ? a-'A' : 'a'<=a && a<='f' ? a-'a' : -1; };
        for (int i=0; i < b.size(); i++) {
761
            char highDigit=toHex(bytes[2+2*i]), lowDigit=toHex(bytes[2+2*i+1]);
762
763
764
765
766
767
768
            if (highDigit<0 || lowDigit<0)
                return false;
            b.append(char(highDigit<<4)+lowDigit);
        }
        return true;
    }
    //! QByteArray may either contain a QVariant serialized by QDataStream or raw bytes
769
    void toVariant(QVariant& v, const QByteArray& b) {
770
771
772
773
774
775
        QDataStream s(b); s >> v;
        if (s.status()==QDataStream::Ok) {
            return;
        }
        v = QVariant(b);
    }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
776
777
778
779
780
781
782
};

// //////////////////////////////////////////////////////////////////////////
// Dynamic and deferred IWriter support

#include <QtCore/qatomic.h>

783
class QLogger : public Cursor
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
784
785
786
787
788
{
    static QAtomicPointer<IWriter> s_impl;
public:
    static IWriter* setWriter(IWriter* i) { return s_impl.fetchAndStoreOrdered(i); }

789
    QLogger() : Cursor(s_impl) {}
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
790
791
};

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
792
// TODO in .cpp QAtomicPointer<IWriter> QLogger::s_impl = nullptr;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
793
794
795

#include <functional>

796
//! A QBind<T,TResult> polymorphic in T (erased at compile-time) which is captured by reference or copy (for rvalue references) to be bound later
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
797
//! \warning a T&& may be allocated dynamically if its size exceeds compiler's Small Function Optimization
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
798
//! \warning copying should be forced using QBindable(T(t)) if QBindable::bind() must be called after t is destructed like when queued to another thread for writing
799
template<class TResult=Cursor>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
800
class QBindable
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
801
802
803
{
    std::function<TResult(Val<TResult>&&)> f;
public:
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
804
805
806
    QBindable() {}
    template<typename T> QBindable(T&  t) : f( /*captured T ref */[&t] (Val<TResult>&& v) { return v.bind(t); } ) {}
    template<typename T> QBindable(T&& t) : f( /*captured T copy*/[ t] (Val<TResult>&& v) { return v.bind(t); } ) {}
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
807
808
809
810
811

    TResult bind(Val<TResult>&& v) { return f(std::move(v)); }
};

// //////////////////////////////////////////////////////////////////////////
812
// QBind partial specializations (generic on TResult, usually not on v->mode() for dynamically-sized or builtin types)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
813

EXT Arnaud Clère's avatar
merged    
EXT Arnaud Clère committed
814
template<class T_>
EXT Arnaud Clère's avatar
WIP    
EXT Arnaud Clère committed
815
template<class Ts>
816
817
818
Seq<T_> Seq<T_>::forEach(Ts& ts,
                         TResult(*itemBind)(typename Ts::value_type&, Val<TResult>&&),
                         bool(*itemPredicate)(const typename Ts::value_type&)) {
819
    if ((*this)->mode()==Write) {
820
821
822
823
        for (auto&& t : ts) {
            if (itemPredicate(t)) {
                itemBind(t, unsafeItem());
            }
EXT Arnaud Clère's avatar
merged    
EXT Arnaud Clère committed
824
        }
825
        return std::move(*this);
EXT Arnaud Clère's avatar
merged    
EXT Arnaud Clère committed
826
    }
827
828
829
830
    else if ((*this)->mode()==Read) {
        auto&& i = ts.begin();
        Val<typename T_::TResult> item;
        while ((item = unsafeItem())) {
EXT Arnaud Clère's avatar
WIP    
EXT Arnaud Clère committed
831
            typename Ts::value_type newItem;
832
833
834
835
836
            if (itemBind(newItem, std::move(item)) && itemPredicate(newItem)) {
                if (i != ts.end()) {
                    *i = newItem;
                }
                else {
837
838
839
840
                    ts.insert(i, newItem);
                }
            }
        }
841
        return std::move(*this);
EXT Arnaud Clère's avatar
merged    
EXT Arnaud Clère committed
842
843
844
845
846
847
848
    }
    else { Q_ASSERT_X(!this, Q_FUNC_INFO, "Unsupported implementation mode()"); return null(); }
}

// //////////////////////////////////////////////////////////////////////////
// QBind partial specializations (generic on TResult, usually not on v->mode() for dynamically-sized or builtin types)

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
849
850
851
#include <QtCore/qmetatype.h>
#include <QtCore/qmetaobject.h>

852
// For QBIND_GADGET_WITH_METAOBJECT below
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
853
854
855
856
template<class T, class TResult>
TResult qbind(Val<TResult>&& v, T* t) {
    auto rw = v->mode();
    auto mo = T::staticMetaObject;
857
    auto r  = v.meta(qmName,QAsciiData(mo.className())).record();
858
    for (int i = 0; r && i<mo.propertyCount(); i++) {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
859
860
861
862
        auto p = mo.property(i);
        if (p.isStored()) {
            if (rw==Read) {
                QVariant pv;
863
864
                QIdentifier id(p.name());
                r = r.item(id).bind(pv);
865
866
867
868
869
870
871
872
873
874
875
876
                if (!r || !pv.isValid() || pv.isNull()) {
                    if (p.isResettable() && !p.resetOnGadget(t)) {
                        v->reportError(qBindIgnoredItem);
                    }
                }
                else {
                    if (p.isEnumType()) {
                        pv.convert(QVariant::Int);
                    }
                    if (!p.writeOnGadget(t, pv)) {
                        v->reportError(qBindIgnoredItem);
                    }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
877
878
                }
            }
879
            else if (rw==Write) {
880
                QVariant pv = p.readOnGadget(t);
881
882
883
884
                if (!pv.isValid()) {
                    v->reportError(qBindIgnoredItem);
                }
                else if (!pv.isNull()) {
885
886
                    QIdentifier id(p.name());
                    auto i = r.item(id);
887
888
                    if (p.isEnumType()) {
                        pv.convert(QVariant::Int);
889
890
891
                        i = i.meta(qmName, p.isFlagType() ?
                            QAsciiData(p.enumerator().valueToKeys(pv.value<int>())) :
                            QAsciiData(p.enumerator().valueToKey (pv.value<int>())));
892
893
894
                    }
                    r = i.bind(pv);
                }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
895
896
897
898
899
900
901
902
903
904
            }
            else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.null(); }
        }
    }
    return r;
}

//! Default bind(Val<TResult>&&) based on static QMetaObject reflection
#define QBIND_GADGET_WITH_METAOBJECT(Class) template<class TResult> TResult bind(Val<TResult>&& v) { return qbind<Class,TResult>(std::move(v), this); }

905
906
template<typename T, class TResult> //=Cursor
struct QBind<QBindDefault<T>, TResult> {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
907
908
    static TResult bind(Val<TResult>&& v, QBindDefault<T>&& t) {
        if (v->mode()==Read) {
909
            v->setChoice(true);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
910
            auto r=v.null();
911
            v->setChoice(false);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
912
913
914
915
916
917
918
919
920
921
922
923
            if (r) {
                t.value = t.defaultValue;
                return r;
            }
        }
        return v.bind(t.value);
    }
    static TResult bind(Val<TResult>&& v, QBindDefault<T>&  t) {
        return bind(std::move(v),std::move(t));
    }
};

924
925
template<typename T, class TResult> //=Cursor
struct QBind<T, TResult, typename std::enable_if<std::is_unsigned<T>::value && std::is_integral<T>::value && !std::is_same<T,bool>::value>::type> {
926
    static TResult bind(Val<TResult>&& v, T&& t) {
927
        return v.bind(quint64(t));
928
    }
929
930
931
    static TResult bind(Val<TResult>&& v, T&  t) {
        if (v->mode()==Write) {
            return bind(std::move(v),T(t));
932
        }
933
        else if (v->mode()==Read) {
934
            quint64 u; auto r=v.bind(u); if (r) { t=u; } return r;
935
        }
936
        else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.null(); }
937
938
    }
};
939
940
template<typename T, class TResult> //=Cursor
struct QBind<T, TResult, typename std::enable_if<std::is_signed<T>::value && std::is_integral<T>::value && !std::is_same<T,bool>::value>::type> {
941
    static TResult bind(Val<TResult>&& v, T&& t) {
942
        return v.bind(qint64(t));
943
    }
944
945
946
    static TResult bind(Val<TResult>&& v, T&  t) {
        if (v->mode()==Write) {
            return bind(std::move(v),T(t));
947
        }