QBind.h 65 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
extern QIdentifierLiteral qBindExpectedItem          ;
extern QIdentifierLiteral qBindExpectedNull          ;
extern QIdentifierLiteral qBindExpectedSequence      ;
extern QIdentifierLiteral qBindExpectedRecord        ;
extern QIdentifierLiteral qBindExpectedText          ;
extern QIdentifierLiteral qBindExpectedBytes         ;
extern QIdentifierLiteral qBindExpectedInteger       ;
extern QIdentifierLiteral qBindExpectedDecimal       ;
extern QIdentifierLiteral qBindExpectedSmallerNumber ;
extern QIdentifierLiteral qBindExpectedPositiveNumber;
extern QIdentifierLiteral qBindExpectedBoolean       ;
extern QIdentifierLiteral qBindExpectedConstant      ;

extern QIdentifierLiteral qBindIgnoredItem           ;
extern QIdentifierLiteral qBindIgnoredItemName       ;
extern QIdentifierLiteral qBindIgnoredCharacter      ;
extern QIdentifierLiteral qBindIgnoredBytes          ;
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
extern QIdentifierLiteral 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
94
//! Contains comma-separated names
extern QIdentifierLiteral qmColumns;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
95
96

//! Sequence of nested non-empty sequences of definite sizes can be implemented as multi-dimensional array
97
98
//! Contains comma-separated natural numbers
extern QIdentifierLiteral qmSizes;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
99
100

//! Sequence of records where item(qmNodes) contains children can be implemented as trees
101
102
//! Contains name of a sequence of records with recursively the same name
extern QIdentifierLiteral qmChildren;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
103

104
//! Name of current data
105
extern QIdentifierLiteral qmName;
106

107
extern QIdentifierLiteral qmColor;
108

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
109
// //////////////////////////////////////////////////////////////////////////
110
// QBind<T,TResult>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
111
112

template<class T_> class Val;
113
114
115
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
116

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
117
118
119
120
121
122
//! 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
//!
123
124
//! \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
125
//! \remark bind is more explicit than operator() but could be changed to enable use as std::function
126
127
128
//! \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
129
//!
130
template<typename T, class TResult=Cursor, typename TEnabledIf=void>
131
struct QBind {
132
133
134
    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&&)
135
};
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
136

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

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

146
147
#include <functional>

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

152
// Custom bind support
153
154
155
156
157
158
#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>&&)>;
159

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

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

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

172
    /**/             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
173

174
175
176
177
    /**/             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_ (); }
178
179
180
181
182
    /**/                 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_ (); }
183
    template<typename T> T_  bind    (             T&& t) { return outer->_bind(std::forward<T>(t)) ?         std::move(outer)  :     T_ (); }
184
    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
185

186
    // Custom bind support
187
188
    /**/                 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_(); }
189

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
190
    // Literal metadata support
191
192
193
194
195
196
197
    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); }
198

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

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
205
206
207
208
209
210
211
212
    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)
213
    explicit Seq(T_&& out) noexcept { std::swap(outer, out); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
214
215

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

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

222
223
    /**/    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
224
225

    // Shortcuts
226
227
228
229
230
231
232
233
234
    /**/             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
235
    template<typename T> Seq<T_>  bind    (             T&& t) { return item().bind(std::forward<T>(t)); }
236
    template<typename T> Rec<T_>  bind    (T& t, T&& defaultT) { return item().bind(t,std::forward<T>(defaultT)); }
237

238
    // Custom bind support
239
240
    /**/                 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_>(); }
241
242
    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; });
243

244
245
    // 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
246
private:
247
    template<typename T, class TResult, typename TEnabledIf> friend struct QBind;
248
    Val<TResult> unsafeItem() noexcept { return outer->_item() ? Val<TResult>(outer->_unsafeCopy()) : Val<TResult>(); }
249
    Seq<TResult> unsafeThis() noexcept { return                  Seq<TResult>(outer->_unsafeCopy())                 ; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
250
251
252
253
254
255
256
257
258

    T_ outer = T_();
};

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

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

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

268
269
270
    /**/    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
271
272

    // Shortcuts
273
274
275
276
277
278
279
280
281
282
    /**/             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)); }
283
284

    // Custom bind support
285
286
    /**/                 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
287
private:
288
    template<typename T, class TResult, typename TEnabledIf> friend struct QBind;
289
    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
290
291
292
293

    T_ outer = T_();
};

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
294
295
296
297
298
299
300
// //////////////////////////////////////////////////////////////////////////
// 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  {};
301
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
302
303
304
305
306
307
308
309
310
311
312
313
314

#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()
//!
315
template<class TImpl> //!< Processing associated to the traversal
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
316
317
318
319
class Cur
{
    Q_DISABLE_COPY(Cur)
public:
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
320
    Q_ENABLE_MOVE_DEFAULT(Cur)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
321
322
323

    using TResult = Cur;

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

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
326
327
328
329
    BindMode    mode() const noexcept { return impl ? impl->mode() : BindMode::Invalid; }
    operator    bool() const noexcept { return impl && impl->_isOk(); } //!< Drives QBind<T>::bind() traversal
    Cur* operator ->()       noexcept { return this; }
    Val<Cur>   value()       noexcept { return Val<Cur>(std::move(*this)); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
330

331
332
333
    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
334
335
336
337
338
protected:
    template<class T_> friend class Val; // enables calling methods below
    template<class T_> friend class Seq;
    template<class T_> friend class Rec;

339
340
341
342
343
344
345
346
347
348
    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
349

350
    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
351
private:
352
    template<typename T> bool _bind(BindNative , T&& t) { return Q_LIKELY(impl) &&   impl->_bind(                        std::forward<T>(t)); }
353
    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
354

355
356
357
358
    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
359
private:
360
    Cur _unsafeCopy() noexcept { return impl ? Cur(impl) : Cur(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
361
362
363
364

    TImpl* impl = nullptr;
};

365
template<class TResult=Cursor>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
366
367
368
369
370
371
372
373
374
375
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
376
// //////////////////////////////////////////////////////////////////////////
377
// Base Cur<TImpl> implementations for Read and Write BindMode
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
378

379
#include <QtCore/qvariant.h>
380
381
382
383
//#include <QtCore/qfloat16.h>
//#include <QtCore/qdatetime.h>
//#include <QtCore/quuid.h>

384
//! Interface for Cur's TImpl with a fixed subset of BindNative types and just a few optional methods
385
386
387
struct IBind {
    virtual ~IBind() = default;

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
388
    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
389
390
391
392
393
394
395

    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;

396
397
398
399
    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
400

401
402
403
404
405
406
407
    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;
408
409
    virtual bool _bind(          QString&  r) = 0;
    virtual bool _bind(             bool&  r) = 0;
410
411
412
413
414
415
416
417
418
419
    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
420
    virtual bool _bind(       QByteArray&  r) = 0;
421
    virtual bool _bind(         QVariant&  r) = 0;
422
423
424

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

425
426
    // Overloads for const& and && T

427
    virtual bool _bind(const   QUtf8Data&  r) = 0;
428
429
430
431
432
433
434
435
436
437
438
439
440
    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;
441
    virtual bool _bind(const    QVariant&  r) = 0;
442

443
    virtual bool _bind(        QUtf8Data&& r) = 0;
444
445
446
447
448
449
450
451
452
453
454
455
456
    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;
457
    virtual bool _bind(         QVariant&& r) = 0;
458

459
460
    virtual bool _any() { return _null(); }

461
462
    //! \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&) {}
463
464

    virtual void _setChoice(bool) {}
465
    virtual void _reportError(QIdentifierLiteral) {}
466
};
467

468
template<> struct BindSupport<  QUtf8Data> : BindNative {};
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
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 {};
488
template<> struct BindSupport<   QVariant> : BindNative {};
489

490
static char *qulltoa2(char *p, qulonglong n, int base=10) //!< Reproduced here for benchmark purposes only
491
492
493
494
495
496
497
498
499
500
{
    *--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;
}
501
static char * qlltoa2(char *p,  qlonglong n, int base=10) //!< Reproduced here for benchmark purposes only
502
503
504
505
506
507
508
509
510
511
{
    if (n < 0) {
        p = qulltoa2(p, qulonglong(-(1 + n)) + 1, base);
        *--p = '-';
    } else {
        p = qulltoa2(p, qulonglong(n), base);
    }
    return p;
}

512
513
#include <QtCore/qdatastream.h>

514
//! Base IBind implementation with BindMode::Write
515
struct IWriter : public IBind
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
516
{
517
    virtual ~IWriter() noexcept = default;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
518

519
    virtual BindMode mode() const noexcept { return BindMode::Write; }
520

521
    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
522

523
524
525
    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
526
527
528

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

531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
    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) {
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
        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) {
574
575
                QIdentifier id(i.key().toString().toLatin1());
                if (!_item(id) || !_bind(QVariant(*i))) {
576
577
578
579
580
                    return false;
                }
            }
            return _out();
        }
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
        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
601
602
603

        // 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
604
        // QByteArray will be encoded into a QVariant to avoid ambiguities when decoding bytes
605
606
607
608
609
610
611
612
613

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

        return _null();
    }
614

615
616
617
618
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
    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
646
647
};

648
//! Base IBind implementations with BindMode::Read
649
struct IReader : public IBind
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
650
{
651
    virtual ~IReader() noexcept = default;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
652

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

655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
    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); }
680
681
682
    virtual bool _bind( QVariant& dst) {
        _setChoice(true);

683
        quint32 size=0; QIdentifier key; QVariant item;
684
685
        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(); }
686
687
688
689
690
        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; }
691
        QUtf8Data  u8; if (_bind(u8)) { dst = QVariant::fromValue(u8); return true; }
692
        QString     t; if (_bind( t)) { dst = QVariant(t); return true; }
693
        _setChoice(false);
694
        if (!_null()) _reportError(QIdentifierLiteral("ExpectedOneOfBooleanDecimalBytesTextSequenceRecordNull"));
695
696
        /**/                          dst = QVariant( ); return true;
    }
697

698
699
700
701
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
    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; }
729

730
731
732
733
734
735
    virtual bool _any() { _setChoice(true);
                          if (  (_sequence() && _out())
                              ||(_record  () && _out())
                              || _null    ()          ) {
                              return true;
                          };
736
737
738
739
                          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;
740
                          QVariant v;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
741
                          QByteArray bytes;
742
                          if (_bind(b) || _bind(d) || _bind(i) || _bind(u) || _bind(v) || _bind(bytes)) {
743
744
                              return true;
                          }
745
                          _setChoice(false);
746
                          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)
747
748
                          return _bind(s);
                        }
749
protected:
750
751
    bool toByteArray(QByteArray& b, QUtf8Data s) {
        const QByteArray& bytes = s.utf8();
752
        if (!bytes.startsWith("0x") || s.size()%2==1) return false;
753
754
755
        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++) {
756
            char highDigit=toHex(bytes[2+2*i]), lowDigit=toHex(bytes[2+2*i+1]);
757
758
759
760
761
762
763
            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
764
    void toVariant(QVariant& v, const QByteArray& b) {
765
766
767
768
769
770
        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
771
772
773
774
775
776
777
};

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

#include <QtCore/qatomic.h>

778
class QLogger : public Cursor
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
779
780
781
782
783
{
    static QAtomicPointer<IWriter> s_impl;
public:
    static IWriter* setWriter(IWriter* i) { return s_impl.fetchAndStoreOrdered(i); }

784
    QLogger() : Cursor(s_impl) {}
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
785
786
};

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
787
// TODO in .cpp QAtomicPointer<IWriter> QLogger::s_impl = nullptr;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
788
789
790

#include <functional>

791
//! 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
792
//! \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
793
//! \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
794
template<class TResult=Cursor>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
795
class QBindable
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
796
797
798
{
    std::function<TResult(Val<TResult>&&)> f;
public:
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
799
800
801
    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
802
803
804
805
806

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

// //////////////////////////////////////////////////////////////////////////
807
// 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
808

EXT Arnaud Clère's avatar
merged    
EXT Arnaud Clère committed
809
template<class T_>
EXT Arnaud Clère's avatar
WIP    
EXT Arnaud Clère committed
810
template<class Ts>
811
812
813
Seq<T_> Seq<T_>::forEach(Ts& ts,
                         TResult(*itemBind)(typename Ts::value_type&, Val<TResult>&&),
                         bool(*itemPredicate)(const typename Ts::value_type&)) {
814
    if ((*this)->mode()==Write) {
815
816
817
818
        for (auto&& t : ts) {
            if (itemPredicate(t)) {
                itemBind(t, unsafeItem());
            }
EXT Arnaud Clère's avatar
merged    
EXT Arnaud Clère committed
819
        }
820
        return std::move(*this);
EXT Arnaud Clère's avatar
merged    
EXT Arnaud Clère committed
821
    }
822
823
824
825
    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
826
            typename Ts::value_type newItem;
827
828
829
830
831
            if (itemBind(newItem, std::move(item)) && itemPredicate(newItem)) {
                if (i != ts.end()) {
                    *i = newItem;
                }
                else {
832
833
834
835
                    ts.insert(i, newItem);
                }
            }
        }
836
        return std::move(*this);
EXT Arnaud Clère's avatar
merged    
EXT Arnaud Clère committed
837
838
839
840
841
842
843
    }
    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
844
845
846
#include <QtCore/qmetatype.h>
#include <QtCore/qmetaobject.h>

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

900
901
template<typename T, class TResult> //=Cursor
struct QBind<QBindDefault<T>, TResult> {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
902
903
    static TResult bind(Val<TResult>&& v, QBindDefault<T>&& t) {
        if (v->mode()==Read) {
904
            v->setChoice(true);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
905
            auto r=v.null();
906
            v->setChoice(false);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
907
908
909
910
911
912
913
914
915
916
917
918
            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));
    }
};

919
920
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> {
921
    static TResult bind(Val<TResult>&& v, T&& t) {
922
        return v.bind(quint64(t));
923
    }
924
925
926
    static TResult bind(Val<TResult>&& v, T&  t) {
        if (v->mode()==Write) {
            return bind(std::move(v),T(t));
927
        }
928
        else if (v->mode()==Read) {
929
            quint64 u; auto r=v.bind(u); if (r) { t=u; } return r;
930
        }
931
        else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.null(); }
932
933
    }
};
934
935
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> {
936
    static TResult bind(Val<TResult>&& v, T&& t) {
937
        return v.bind(qint64(t));
938
    }
939
940
941
    static TResult bind(Val<TResult>&& v, T&  t) {
        if (v->mode()==Write) {
            return bind(std::move(v),T(t));
942
        }
943
        else if (v->mode()==Read) {
944
            qint64 u; auto r=v.bind(u); if (r) { t=u; } return r;
945
        }
946
        else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.null(); }
947
948
    }
};
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
949

950