Vous avez reçu un message "Your GitLab account has been locked ..." ? Pas d'inquiétude : lisez cet article https://docs.gricad-pages.univ-grenoble-alpes.fr/help/unlock/

QValue.h 71 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

48
49
#include <QtCore/qvariant.h>

50
#include "QData.h"
51

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
52
53
54
// //////////////////////////////////////////////////////////////////////////
// Standard error names

55
extern QIdentifierLiteral qBindUnexpectedValue; //!< Values that cannot be bound (invalid or not supported by the bound data type or our data model)
56
57
extern QIdentifierLiteral qBindUnexpectedEnd;

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
58
59
60
61
62
63
64
65
66
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;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
67
extern QIdentifierLiteral qBindExpectedPositiveInteger;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
68
69
extern QIdentifierLiteral qBindExpectedBoolean;
extern QIdentifierLiteral qBindExpectedConstant;
70

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
71
72
73
74
extern QIdentifierLiteral qBindIgnoredItem;
extern QIdentifierLiteral qBindIgnoredItemName;
extern QIdentifierLiteral qBindIgnoredCharacter;
extern QIdentifierLiteral qBindIgnoredBytes;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
75
76
77
78

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

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

#include <QtCore/qstring.h>

86
// 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
87

88
extern QIdentifierLiteral qmDataStreamVersion; //!< Allows QAbstractValue support of QDataStream
89

90
// N-dimensional data structures for which specific QAbstractValueWriter may have optimized implementations
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
91
// BEWARE though that nested calls to QAbstractValueWriter are not guaranteed to follow the declared structure (this would prevent reusing general QTransmogrifier functors for nested data structures)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
92
93

//! Sequence of records can be implemented as table with columns below (record item names are always the same in a fixed order)
94
95
//! Contains comma-separated names
extern QIdentifierLiteral qmColumns;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
96
97

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

//! Sequence of records where item(qmNodes) contains children can be implemented as trees
102
103
//! 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
104

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

108
extern QIdentifierLiteral qmColor;
109

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
110
// //////////////////////////////////////////////////////////////////////////
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
111
// QTransmogrifier<T>
112

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
113
enum QValueMode { Invalid=0, Read=1, Write=2 }; //!< Specifies QTransmogrifier::zap traversal and processing (the design would support other QValueMode like Append or Diff)
114

115
using QValueErrorHandler = std::function<bool(QIdentifierLiteral,QString)>;
116

117
118
struct BindGeneric {};
struct BindNative  {};
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
119
//!< Specifies whether QVal calls QAbstractValue::tryBind or QTransmogrifier<T>::bind
120
//!< \remark It would be possible to remove BindSupport by defining all QAbstractValue overloads in QVal
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
template<typename T, typename TEnabledIf=void> struct BindSupport : BindGeneric {};

template<> struct BindSupport<  QUtf8Data> : BindNative {};
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 {};
template<> struct BindSupport<   QVariant> : BindNative {};

#include <QtCore/qvariant.h>
//#include <QtCore/qfloat16.h>
//#include <QtCore/qdatetime.h>
//#include <QtCore/quuid.h>

150
//! Interface for QValue implementations with a fixed subset of BindNative types and just a few optional methods
151
152
struct QAbstractValue {
    virtual ~QAbstractValue() = default;
153

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
154
    virtual QValueMode mode() const noexcept = 0; //!< \remark a static constexpr QValueMode Mode did not exhibit noticeable performance improvements and may trigger twice more code generation for Read/Write independant QTransmogrifier like Person::bind
155

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
156
    virtual bool isValid() const noexcept = 0; //!< Current operation status
157

158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
    virtual bool trySequence(quint32* size=nullptr) = 0;
    virtual bool tryRecord  (quint32* size=nullptr) = 0;

    virtual bool tryItem(                    ) = 0;
    virtual bool tryItem(QIdentifier&       n) = 0;
    virtual bool tryItem(QIdentifierLiteral n) = 0;
    virtual bool tryOut (                    ) = 0; //!< End of sequence or record

    virtual bool tryBind(     QUtf8DataView u) = 0;
    virtual bool tryBind(       const char* u) = 0;
    virtual bool tryBind(    QAsciiDataView a) = 0;
    virtual bool tryBind(     QLatin1String l) = 0;
    virtual bool tryBind(       QStringView s) = 0;

    virtual bool tryBind(       QUtf8Data&  r) = 0;
    virtual bool tryBind(         QString&  r) = 0;
    virtual bool tryBind(            bool&  r) = 0;
    virtual bool tryBind(           qint8&  r) = 0; //!< \warning Must return false instead of losing sign or digit
    virtual bool tryBind(          quint8&  r) = 0; //!< \warning Must return false instead of losing sign or digit
    virtual bool tryBind(          qint16&  r) = 0; //!< \warning Must return false instead of losing sign or digit
    virtual bool tryBind(         quint16&  r) = 0; //!< \warning Must return false instead of losing sign or digit
    virtual bool tryBind(          qint32&  r) = 0; //!< \warning Must return false instead of losing sign or digit
    virtual bool tryBind(         quint32&  r) = 0; //!< \warning Must return false instead of losing sign or digit
    virtual bool tryBind(          qint64&  r) = 0; //!< \warning Must return false instead of losing sign or digit
    virtual bool tryBind(         quint64&  r) = 0; //!< \warning Must return false instead of losing sign or digit
    virtual bool tryBind(           float&  r) = 0; //!< \warning Must return false instead of losing sign or digit
    virtual bool tryBind(          double&  r) = 0; //!< \warning Must return false instead of losing sign or digit
    virtual bool tryBind(      QByteArray&  r) = 0;
    virtual bool tryBind(        QVariant&  r) = 0;
187
188
189
190
191

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

    // Overloads for const& and && T

192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
    virtual bool tryBind(const  QUtf8Data&  r) = 0;
    virtual bool tryBind(const    QString&  r) = 0;
    virtual bool tryBind(const       bool&  r) = 0;
    virtual bool tryBind(const      qint8&  r) = 0;
    virtual bool tryBind(const     quint8&  r) = 0;
    virtual bool tryBind(const     qint16&  r) = 0;
    virtual bool tryBind(const    quint16&  r) = 0;
    virtual bool tryBind(const     qint32&  r) = 0;
    virtual bool tryBind(const    quint32&  r) = 0;
    virtual bool tryBind(const     qint64&  r) = 0;
    virtual bool tryBind(const    quint64&  r) = 0;
    virtual bool tryBind(const      float&  r) = 0;
    virtual bool tryBind(const     double&  r) = 0;
    virtual bool tryBind(const QByteArray&  r) = 0;
    virtual bool tryBind(const   QVariant&  r) = 0;

    virtual bool tryBind(       QUtf8Data&& r) = 0;
    virtual bool tryBind(         QString&& r) = 0;
    virtual bool tryBind(            bool&& r) = 0;
    virtual bool tryBind(           qint8&& r) = 0;
    virtual bool tryBind(          quint8&& r) = 0;
    virtual bool tryBind(          qint16&& r) = 0;
    virtual bool tryBind(         quint16&& r) = 0;
    virtual bool tryBind(          qint32&& r) = 0;
    virtual bool tryBind(         quint32&& r) = 0;
    virtual bool tryBind(          qint64&& r) = 0;
    virtual bool tryBind(         quint64&& r) = 0;
    virtual bool tryBind(           float&& r) = 0;
    virtual bool tryBind(          double&& r) = 0;
    virtual bool tryBind(      QByteArray&& r) = 0;
    virtual bool tryBind(        QVariant&& r) = 0;

224
225
    virtual bool tryNull() = 0; //!< Null values are used where the information is irrelevant like in optional values or nullptr
    virtual bool tryAny () = 0; //!< Any value is used where the information is unknown or undefined
226
227
228
229

    //! \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&) {}

230
231
232
233
    virtual QValueErrorHandler setErrorHandler(QValueErrorHandler newHandler = nullptr) { auto previousHandler = errorHandler; errorHandler = newHandler; return previousHandler; }
    virtual bool handleError(QIdentifierLiteral e, QString context = QString()) = 0;
protected:
    QValueErrorHandler errorHandler = nullptr;
234
235
236
237
};

// //////////////////////////////////////////////////////////////////////////
// Default TResult implementations
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
238

239
template<class T_> class QVal;
240

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

244
//! A QValueEnd represents the end of a QValue traversal and processing
245
//!
246
//! \warning isValid does not take into account intermediate errors that are handled by QValueErrorHandler
247
//!
248
249
250
251
252
253
254
255
256
class QValueEnd {
public:
    QValueEnd() = default;
    QValueEnd(bool result) : status(result) {}
    bool isValid() { return status; }
    explicit operator bool() { return isValid(); }
private:
    bool status = false;
};
257

258
//! A QCur is a cursor inside a structured value implemented as a move-only, non-owning pointer to QAbstractValue
259
//!
260
261
//! Its public interface only allows to check the status of QAbstractValue calls using explicit operator bool(), and to handleError().
//! Its protected interface is used by QVal, QSeq, QRec to only allow QAbstractValue calls from the previous valid QCur.
262
//!
263
//! It allows to easily add QTransmogrifier support to existing Reader/Writers by adding at least one value() method returning a QCur(this).value()
264
//! \see TextWriter in main.cpp
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
265
//!
266
267
//! \remark QCur allows using a fluent interface without checking intermediate status by setting impl=nullptr on error
//! and subsequently bypassing structured value impl calls. Errors can result from:
268
269
//! - using the same intermediate QVal/QSeq/QRec twice (a programmer error), or
//! - runtime impl errors like trying to bind a trySequence() whereas the data read matches a tryRecord()
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
270
//!
271
class QCur
272
{
273
    Q_DISABLE_COPY(QCur)
274
public:
275
276
277
    QCur           (        ) noexcept : impl(nullptr) {}
    QCur           (QCur&& o) noexcept : impl(o.impl) { o.impl = nullptr; }
    QCur& operator=(QCur&& o) noexcept { auto prev = impl; impl = o.impl; o.impl = prev; return *this; }
278

279
    explicit QCur(QAbstractValue* i) : impl(i) { Q_ASSERT(impl); }
280

281
    QValueMode        mode() const noexcept { return impl ? impl->mode() : QValueMode::Invalid; }
282
283
    explicit operator bool() const noexcept { return isValid(); }
    bool           isValid() const noexcept { return impl && impl->isValid(); } //!< Drives QTransmogrifier<T>::bind() traversal
284
285
    QCur*      operator ->()       noexcept { return this; }
    QVal<QCur>       value()       noexcept ;
286
    operator QValueEnd() {
287
        bool isValid = impl && impl->isValid();
288
        impl = nullptr;
289
290
        return QValueEnd{isValid};
    }
291

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
292
    QValueErrorHandler setErrorHandler(QValueErrorHandler newHandler = nullptr) { return Q_LIKELY(impl) ? impl->setErrorHandler(newHandler) : nullptr; }
293
294
    bool handleError(QIdentifierLiteral name, QString context = QString()) { return Q_LIKELY(impl) ? impl->handleError(name, context) : false; }
    bool handleError(const char*   asciiName, QString context = QString()) { return handleError(QIdentifierLiteral(asciiName), context); }
295
protected:
296
297
298
    template<class T_> friend class QVal; // enables calling methods below
    template<class T_> friend class QSeq;
    template<class T_> friend class QRec;
299
300
301

    void _meta    (QIdentifierLiteral& n,
                   QAsciiData&         m) { if    (Q_LIKELY(impl) )  impl->_meta  (n,m); } //!< idempotent (can be called by more than one code)
302
303
304
    bool trySequence(   quint32* s=nullptr) { return Q_LIKELY(impl) && impl->trySequence(s); }
    bool tryRecord  (   quint32* s=nullptr) { return Q_LIKELY(impl) && impl->tryRecord  (s); }
    bool tryNull    (                     ) { return Q_LIKELY(impl) && impl->tryNull    ( ); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
305
    bool tryAny     (                     ) { return Q_LIKELY(impl) && impl->tryAny     ( ); }
306
307
308
309
    bool tryBind    (      QUtf8DataView u) { return Q_LIKELY(impl) && impl->tryBind    (u); }
    bool tryBind    (     QAsciiDataView a) { return Q_LIKELY(impl) && impl->tryBind    (a); }
    bool tryBind    (      QLatin1String l) { return Q_LIKELY(impl) && impl->tryBind    (l); }
    bool tryBind    (        QStringView u) { return Q_LIKELY(impl) && impl->tryBind    (u); }
310

311
    template<typename T> bool tryBind(             T&& t) { return tryBind(BindSupport<RemoveCvRef<T>>(), std::forward<T>(t)); }
312
private:
313
314
    template<typename T> bool tryBind(BindNative , T&& t) { return Q_LIKELY(impl) && impl->tryBind(       std::forward<T>(t)); }
    template<typename T> bool tryBind(BindGeneric, T&& t) ;
315

316
317
318
319
    bool tryItem(QIdentifierLiteral n) { return Q_LIKELY(impl) && impl->tryItem(n); }
    bool tryItem(QIdentifier&       n) { return Q_LIKELY(impl) && impl->tryItem(n); }
    bool tryItem(                    ) { return Q_LIKELY(impl) && impl->tryItem( ); }
    bool tryOut (                    ) { return Q_LIKELY(impl) && impl->tryOut ( ); }
320
private:
321
    QCur _unsafeCopy() noexcept { return impl ? QCur(impl) : QCur(); }
322

323
    QAbstractValue* impl = nullptr;
324
};
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
325

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

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
329
330
331
332
333
334
// //////////////////////////////////////////////////////////////////////////
// Generic fluent interface for traversing structured values and processing them along the way:
// - serializing, deserializing
// - constructing generic in-memory data structures
// - etc...

335
336
template<class T_> class QRec; //!< a Record   data structure defined below
template<class T_> class QSeq; //!< a Sequence data structure defined below
337
template<class T_> class QVal; //!< a choice of sequence(), record(), null(), or values with at least a textual representation and possibly binary ones
338

339
340
341
using QValue    = QVal<QCur>;
using QSequence = QSeq<QCur>;
using QRecord   = QRec<QCur>;
342

343
// Custom bind support
344
345
346

#include <functional>

347
#ifndef NO_COMPILER_RTTI_OR_EXCEPTIONS
348
349
template<typename T> using QValFunction = QValueEnd(*)(T &,QValue   &&) ;
template<class   Ts> using QSeqFunction = QSequence(*)(Ts&,QSequence&&) ;
350
#endif
351
352
using QValLambda = std::function<QValueEnd(QValue   &&)>;
using QSeqLambda = std::function<QSequence(QSequence&&)>;
353

354
template<class T_> class QVal
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
355
{
356
    Q_DISABLE_COPY(QVal)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
357
public:
358
359
    Q_ENABLE_MOVE_DEFAULT(QVal)
    explicit QVal(T_&& out) noexcept { std::swap(outer, out); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
360

361
362
    explicit operator bool() const noexcept { return isValid(); }
    bool           isValid() const noexcept { return outer.operator bool(); } //!< Drives QTransmogrifier<T>::bind() traversal
363
    QCur*       operator->()       noexcept { return outer.operator   ->(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
364

365
    QVal<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
366

367
368
    QSeq<T_> sequence(quint32* s=nullptr) { return outer->trySequence          (s)  ? QSeq<T_>(std::move(outer)) : QSeq<T_>(); }
    QRec<T_> record  (quint32* s=nullptr) { return outer->tryRecord            (s)  ? QRec<T_>(std::move(outer)) : QRec<T_>(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
369
370
    /**/ T_  null    (                  ) { return outer->tryNull              ( )  ?          std::move(outer)  :      T_ (); }
    /**/ T_  any     (                  ) { return outer->tryAny               ( )  ?          std::move(outer)  :      T_ (); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
371

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
372
373
374
375
376
    /**/ T_  bind    (     const char* u) { return outer->tryBind(QUtf8DataView(u)) ?          std::move(outer)  :      T_ (); }
    /**/ T_  bind    (   QUtf8DataView u) { return outer->tryBind              (u)  ?          std::move(outer)  :      T_ (); }
    /**/ T_  bind    (  QAsciiDataView a) { return outer->tryBind              (a)  ?          std::move(outer)  :      T_ (); }
    /**/ T_  bind    (   QLatin1String l) { return outer->tryBind              (l)  ?          std::move(outer)  :      T_ (); }
    /**/ T_  bind    (     QStringView u) { return outer->tryBind              (u)  ?          std::move(outer)  :      T_ (); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
377

378
379
    template<typename T> T_  bind(             T&& t) { return outer->tryBind(std::forward <T>(t         )) ? std::move(outer) : T_(); }
    template<typename T> T_  bind(T& t, T&& defaultT) { return outer->tryBind(QDefaultValue<T>{t,defaultT}) ? std::move(outer) : T_(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
380

381
    // Custom bind support
382
383
    /**/                 T_  with(      QValLambda      customBind) { return customBind(   std::move(unsafeThis())).isValid() ? std::move(outer) : T_(); }
    template<typename T> T_  with(T& t, QValFunction<T> customBind) { return customBind(t, std::move(unsafeThis())).isValid() ? std::move(outer) : T_(); }
384

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
385
    // Literal metadata support
386
387
388
389
390
    QVal<T_> meta(const char*                n, const char*        m) { return meta(QIdentifierLiteral(n),QAsciiData(m)); }
    QVal<T_> meta(      QIdentifierLiteral&& n,       QAsciiData&& m) { QIdentifierLiteral nref=n; QAsciiData mref=           m ; return meta(nref, mref); }
    QVal<T_> meta(const QIdentifierLiteral&  n,       QAsciiData&& m) { QIdentifierLiteral nref=n; QAsciiData mref=           m ; return meta(nref, mref); }
    QVal<T_> meta(const QIdentifierLiteral&  n, const char*        m) { QIdentifierLiteral nref=n; QAsciiData mref=QAsciiData(m); return meta(nref, mref); }
    QVal<T_> meta(      QIdentifierLiteral&& n, const QAsciiData&  m) { QIdentifierLiteral nref=n; QAsciiData mref=           m ; return meta(nref, mref); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
391

392
393
394
    QSeq<T_> sequence(quint32     s) { return sequence(&s); }
    QRec<T_> record  (quint32     s) { return record  (&s); }
    QRec<T_> record  (const char* n) { return meta(qmName,QAsciiData(n)).record(); }
395

396
    // Shortcuts
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
397
    template<typename T> QSeq<T_> operator<<(T&& t) { return sequence().bind(std::forward<T>(t)); } // stream compatible
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
398
private:
399
    QValue unsafeThis() noexcept { return QValue(outer->_unsafeCopy()); }
400

401
    T_ outer = T_(); //!< moved context of current traversal up to QCur that will point to the value itself (be it a QIODevice or QCborValue, ...)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
402
403
};

404
template<class T_> class QSeq
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
405
{
406
    Q_DISABLE_COPY(QSeq)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
407
public:
408
    Q_ENABLE_MOVE_DEFAULT(QSeq)
409
    ~QSeq() {if (operator QValueEnd().isValid()) { Q_UNUSED(outer->handleError(qBindUnexpectedEnd)); } }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
410

411
412
    explicit operator bool() const noexcept { return isValid(); }
    bool           isValid() const noexcept { return outer.operator bool(); } //!< Drives QTransmogrifier<T>::bind() traversal
413
414
    QCur*       operator->()       noexcept { return outer.operator   ->(); }
    operator     QValueEnd()                { return out(); /* calls T_::operator QValueEnd() if T_ != QValueEnd */ }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
415

416
    /**/      T_    out() { return outer-> tryOut() ?                std::move(outer)  :           T_  (); }
417
    QVal<QSeq<T_>> item() { return outer->tryItem() ? QVal<QSeq<T_>>(std::move(*this)) : QVal<QSeq<T_>>(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
418
419

    // Shortcuts
420
421
    QSeq<QSeq<T_>> sequence(quint32* s=nullptr) { return item().sequence          (s) ; }
    QRec<QSeq<T_>> record  (quint32* s=nullptr) { return item().record            (s) ; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
422
423
    /**/ QSeq<T_>  null    (                  ) { return item().null              ( ) ; }
    /**/ QSeq<T_>  any     (                  ) { return item().any               ( ) ; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
424

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
425
426
427
428
429
    /**/ QSeq<T_>  bind    (     const char* u) { return item().bind(QUtf8DataView(u)); }
    /**/ QSeq<T_>  bind    (   QUtf8DataView u) { return item().bind              (u) ; }
    /**/ QSeq<T_>  bind    (  QAsciiDataView a) { return item().bind              (a) ; }
    /**/ QSeq<T_>  bind    (   QLatin1String l) { return item().bind              (l) ; }
    /**/ QSeq<T_>  bind    (     QStringView u) { return item().bind              (u) ; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
430

431
432
    template<typename T> QSeq<T_>  bind    (             T&& t) { return item().bind(std::forward<T>(t)); }
    template<typename T> QRec<T_>  bind    (T& t, T&& defaultT) { return item().bind(t,std::forward<T>(defaultT)); }
433

434
    // Custom bind support
435
436
    /**/                 QSeq<T_>  with    (        QSeqLambda       customBind) { return bool(customBind(    std::move(unsafeThis()))) ? std::move(*this) : QSeq<T_>(); }
    template<class   Ts> QSeq<T_>  with    (Ts& ts, QSeqFunction<Ts> customBind) { return bool(customBind(ts, std::move(unsafeThis()))) ? std::move(*this) : QSeq<T_>(); }
437
438
    template<class   Ts> QSeq<T_>  forEach (Ts& ts, QValueEnd(*)(typename Ts::value_type&, QValue&&)
                                                  , bool(*)(const typename Ts::value_type&) = [](const typename Ts::value_type&) { return true; });
439

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
440
    // Shortcut
441
    template<typename T> QSeq<T_> operator<<(T&& t) { return item().bind(std::forward<T>(t)); } // stream compatible
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
442
private:
443
444
445
    template<class T_> friend class QVal;
    explicit QSeq(T_&& out) noexcept { std::swap(outer, out); }

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
446
    template<typename T, typename TEnabledIf> friend struct QTransmogrifier;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
447
448
    QValue    unsafeItem() noexcept { return outer->tryItem() ? QValue(outer->_unsafeCopy()) : QValue(); }
    QSequence unsafeThis() noexcept { return                 QSequence(outer->_unsafeCopy())           ; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
449
450
451
452

    T_ outer = T_();
};

453
template<class T_> class QRec
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
454
{
455
    Q_DISABLE_COPY(QRec)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
456
public:
457
    Q_ENABLE_MOVE_DEFAULT(QRec)
458
    ~QRec() { if (operator QValueEnd().isValid()) { Q_UNUSED(outer->handleError(qBindUnexpectedEnd)); } }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
459

460
461
    explicit operator bool() const noexcept { return isValid(); }
    bool           isValid() const noexcept { return outer.operator bool(); } //!< Drives QTransmogrifier<T>::bind() traversal
462
463
    QCur*       operator->()       noexcept { return outer.operator   ->(); }
    operator     QValueEnd()                { return out(); /* calls T_::operator QValueEnd() if T_ != QValueEnd */ }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
464

465
    /**/      T_   out (              ) { return outer->tryOut (                     ) ?                std::move(outer)  :           T_  (); }
466
467
    QVal<QRec<T_>> item(QIdentifier& n) { return outer->tryItem(                   n ) ? QVal<QRec<T_>>(std::move(*this)) : QVal<QRec<T_>>(); }
    QVal<QRec<T_>> item(const char*  n) { return outer->tryItem(QIdentifierLiteral(n)) ? QVal<QRec<T_>>(std::move(*this)) : QVal<QRec<T_>>(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
468
469

    // Shortcuts
470
471
472
473
474
475
476
477
478
479
480
    QSeq<QRec<T_>> sequence(const char* n, quint32* s=nullptr) { auto i = item(n); if (i) { return i.sequence          (s) ; } Q_UNUSED(outer->handleError(qBindIgnoredItem, n)); return QSeq<QRec<T_>>(); }
    QRec<QRec<T_>> record  (const char* n, quint32* s=nullptr) { auto i = item(n); if (i) { return i.record            (s) ; } Q_UNUSED(outer->handleError(qBindIgnoredItem, n)); return QRec<QRec<T_>>(); }
    /**/ QRec<T_>  null    (const char* n                    ) { auto i = item(n); if (i) { return i.null              ( ) ; } Q_UNUSED(outer->handleError(qBindIgnoredItem, n)); return      QRec<T_> (); }
    /**/ QRec<T_>  bind    (const char* n,      const char* u) { auto i = item(n); if (i) { return i.bind(QUtf8DataView(u)); } Q_UNUSED(outer->handleError(qBindIgnoredItem, n)); return      QRec<T_> (); }
    /**/ QRec<T_>  bind    (const char* n,    QUtf8DataView u) { auto i = item(n); if (i) { return i.bind              (u) ; } Q_UNUSED(outer->handleError(qBindIgnoredItem, n)); return      QRec<T_> (); }
    /**/ QRec<T_>  bind    (const char* n,   QAsciiDataView a) { auto i = item(n); if (i) { return i.bind              (a) ; } Q_UNUSED(outer->handleError(qBindIgnoredItem, n)); return      QRec<T_> (); }
    /**/ QRec<T_>  bind    (const char* n,    QLatin1String l) { auto i = item(n); if (i) { return i.bind              (l) ; } Q_UNUSED(outer->handleError(qBindIgnoredItem, n)); return      QRec<T_> (); }
    /**/ QRec<T_>  bind    (const char* n,      QStringView u) { auto i = item(n); if (i) { return i.bind              (u) ; } Q_UNUSED(outer->handleError(qBindIgnoredItem, n)); return      QRec<T_> (); }

    template<typename T> QRec<T_>  bind    (const char* n,              T&& t) { auto i = item(n); if (i) { return i.bind(   std::forward<T>(       t)); } Q_UNUSED(outer->handleError(qBindIgnoredItem, n)); return QRec<T_>(); }
    template<typename T> QRec<T_>  bind    (const char* n, T& t, T&& defaultT) { auto i = item(n); if (i) { return i.bind(t, std::forward<T>(defaultT)); } Q_UNUSED(outer->handleError(qBindIgnoredItem, n)); return QRec<T_>(); }
481
482

    // Custom bind support
483
484
    /**/                 QRec<T_>  with    (const char* n,       QValLambda      customBind) { auto i = item(n); if (i) { return i.with(   customBind); } Q_UNUSED(outer->handleError(qBindIgnoredItem, n)); return QRec<T_>(); }
    template<typename T> QRec<T_>  with    (const char* n, T& t, QValFunction<T> customBind) { auto i = item(n); if (i) { return i.bind(t, customBind); } Q_UNUSED(outer->handleError(qBindIgnoredItem, n)); return QRec<T_>(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
485
private:
486
487
488
    template<class T_> friend class QVal;
    explicit QRec(T_&& out) noexcept { std::swap(outer, out); }

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
489
    template<typename T, typename TEnabledIf> friend struct QTransmogrifier;
490
    QValue unsafeItem(QIdentifier& n) noexcept { return outer->tryItem(n) ? QValue(outer._unsafeCopy()) : QValue(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
491
492
493
494

    T_ outer = T_();
};

495
//! QTransmogrifier is a class of template methods binding T parts with QValue obeying a simple generic data model, and returning a QValueEnd
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
496
//! Each QTransmogrifier<_,T> should provide bind(T&&) and bind(T&) overloads for convenience and may provide bind(const T&) for performance depending on T
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
497
//!
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
498
//! Its least specialized definition calls a T::bind(QValue&&) method that is more convenient to define than a QTransmogrifier specialization
499
//! \see Person::bind() definition in main.cpp for an example
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
500
//!
501
//! \remark bind is not a template function to avoid conflicts with ADL and allow more precise class specialization
502
//! \remark QValue is a move-only type passed by rvalue reference following http://scottmeyers.blogspot.com/2014/07/should-move-only-types-ever-be-passed.html
503
504
//!
template<typename T, typename TEnabledIf=void>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
505
struct QTransmogrifier {
506
507
508
    static QValueEnd zap(QValue&& value,      T&  t) { return                t .zap(std::move(value)); } // In case of error, define a T::bind(QValue) method or external QTransmogrifier<T>::bind(QValue,T&)
    static QValueEnd zap(QValue&& value,const T&  t) { return const_cast<T&>(t).zap(std::move(value)); } // In case of error, define a T::bind(QValue) method or external QTransmogrifier<T>::bind(QValue,const T&)
    static QValueEnd zap(QValue&& value,      T&& t) { return                t .zap(std::move(value)); } // In case of error, define a T::bind(QValue) method or external QTransmogrifier<T>::bind(QValue,T&&)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
509
510
};

511
template<class T>
512
bool QCur::tryBind(BindGeneric, T&& t) { return QTransmogrifier<RemoveCvRef<T>>::zap(QValue(_unsafeCopy()),std::forward<T>(t)).isValid(); }
513

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
514
// //////////////////////////////////////////////////////////////////////////
515
// Base QAbstractValue implementations for Read and Write QValueMode
516
static char *qulltoa2(char *p, qulonglong n, int base=10) //!< Reproduced here for benchmark purposes only
517
518
519
520
521
522
523
524
525
526
{
    *--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;
}
527
static char * qlltoa2(char *p,  qlonglong n, int base=10) //!< Reproduced here for benchmark purposes only
528
529
530
531
532
533
534
535
536
537
{
    if (n < 0) {
        p = qulltoa2(p, qulonglong(-(1 + n)) + 1, base);
        *--p = '-';
    } else {
        p = qulltoa2(p, qulonglong(n), base);
    }
    return p;
}

538
539
#include <QtCore/qdatastream.h>

540
541
//! Base QAbstractValue implementation with QValueMode::Write
struct QAbstractValueWriter : public QAbstractValue
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
542
{
543
    virtual ~QAbstractValueWriter() noexcept = default;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
544

545
    virtual QValueMode mode() const noexcept { return QValueMode::Write; }
546

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

549
550
551
    virtual bool tryItem(                    ) = 0;
    virtual bool tryItem(QIdentifier&       n) = 0;
    virtual bool tryItem(QIdentifierLiteral n) { QIdentifier id(n); return tryItem(id); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
552
553

    //! End of sequence or record
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
    //! Few QAbstractValueWriter need to process this (contiguous QAbstractValueWriter need to mark the end of indefinite sequences and records for instance)
    virtual bool tryOut () { return true; }

    virtual bool tryBind( QUtf8DataView u) = 0;
    virtual bool tryBind(   const char* u) { return tryBind(QUtf8DataView(u)); } // required to match better than QString overloads
    virtual bool tryBind(QAsciiDataView a) { return tryBind(QUtf8DataView(a.data(), a.size())); }
    virtual bool tryBind( QLatin1String l) { return tryBind(QString(l)); }
    virtual bool tryBind(   QStringView s) { return tryBind(QUtf8DataView(s.toUtf8())); }

    virtual bool tryBind(   QUtf8Data&& s) { return tryBind(QUtf8DataView(s)); }
    virtual bool tryBind(     QString&& s) { return tryBind(QStringView(s.data(), s.size())); }
    virtual bool tryBind(        bool&& s) { return tryBind(s ? QAsciiDataView("true") : QAsciiDataView("false")); }
    virtual bool tryBind(       qint8&& n) { return tryBind( qint64(n)); }
    virtual bool tryBind(      quint8&& n) { return tryBind(quint64(n)); }
    virtual bool tryBind(      qint16&& n) { return tryBind( qint64(n)); }
    virtual bool tryBind(     quint16&& n) { return tryBind(quint64(n)); }
    virtual bool tryBind(      qint32&& n) { return tryBind( qint64(n)); }
    virtual bool tryBind(     quint32&& n) { return tryBind(quint64(n)); }
    virtual bool tryBind(      qint64&& n) { const int s=66; char c[s]; char* start= qlltoa2(c+s, n); return tryBind(QAsciiDataView(start,(c+s)-start-1)); }
    virtual bool tryBind(     quint64&& n) { const int s=66; char c[s]; char* start=qulltoa2(c+s, n); return tryBind(QAsciiDataView(start,(c+s)-start-1)); }
    virtual bool tryBind(       float&& n) { static QByteArray s; s.setNum(double(n),'g',std::numeric_limits<   float>::max_digits10); return tryBind(QAsciiDataView(s.constData(),s.size())); } // with specific precision
    virtual bool tryBind(      double&& n) { static QByteArray s; s.setNum(       n ,'g',std::numeric_limits<  double>::max_digits10); return tryBind(QAsciiDataView(s.constData(),s.size())); } // with specific precision
    virtual bool tryBind(  QByteArray&& s) { QByteArray h; h.reserve(s.size()*2+2+1); h.append("0x").append(s.toHex())               ; return tryBind(QAsciiDataView(h.constData(),h.size())); }
    virtual bool tryBind(  QVariant&& src) {
578
579
580
        if (src.canConvert<QVariantList>()) {
            QSequentialIterable ts = src.value<QSequentialIterable>();
            quint32 size=quint32(ts.size());
581
            if (!trySequence(&size)) {
582
583
584
                return false;
            }
            for (QVariant t : ts) {
585
                if (!tryItem() || !tryBind(QVariant(t))) {
586
587
588
                    return false;
                }
            }
589
            return tryOut();
590
        }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
591
        if (src.canConvert<QVariantMap >()) { // TODO _meta(qmColumns,"key,value") trySequence() ...
592
593
            QAssociativeIterable ts = src.value<QAssociativeIterable>();
            quint32 size=quint32(ts.size());
594
            if (!tryRecord(&size)) {
595
596
597
598
599
                return false;
            }
            QAssociativeIterable::const_iterator i = ts.begin();
            const QAssociativeIterable::const_iterator end = ts.end();
            for ( ; i != end; ++i) {
600
                QIdentifier id(i.key().toString().toLatin1());
601
                if (!tryItem(id) || !tryBind(QVariant(*i))) {
602
603
604
                    return false;
                }
            }
605
            return tryOut();
606
        }
607
        if (src.userType() // TODO src.type if QUtf8Data becomes a Qt type
608
609
610
611
612
613
                      ==qMetaTypeId<QUtf8Data>()) return tryBind(src.value<  QUtf8Data>());
        if (src.type()==QVariant::String        ) return tryBind(src.value<    QString>());
        if (src.type()==QVariant::Char          ) return tryBind(src.          toString());
        if (src.type()==QVariant::Bool          ) return tryBind(src.value<       bool>());
        if (src.type()==QVariant::LongLong      ) return tryBind(src.value<  qlonglong>()); // or  qint64 to be sure to call the good tryBind()?
        if (src.type()==QVariant::ULongLong     ) return tryBind(src.value< qulonglong>()); // or quint64 to be sure to call the good tryBind()?
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
614
615
616
617
618
619
620
        if (src.type()==int(QMetaType::ULong   )) return tryBind(src.value<    quint32>());
        if (src.type()==int(QMetaType::Long    )) return tryBind(src.value<     qint32>());
        if (src.type()==int(QMetaType::Short   )) return tryBind(src.value<      short>());
        if (src.type()==int(QMetaType::Char    )) return tryBind(src.value<       char>());
        if (src.type()==int(QMetaType::UShort  )) return tryBind(src.value<     ushort>());
        if (src.type()==int(QMetaType::UChar   )) return tryBind(src.value<      uchar>());
        if (src.type()==int(QMetaType::SChar   )) return tryBind(src.value<signed char>());
621
622
623
        if (src.type()==QVariant::UInt          ) return tryBind(src.value<       uint>());
        if (src.type()==QVariant::Int           ) return tryBind(src.value<        int>());
        if (src.type()==QVariant::Double        ) return tryBind(src.value<     double>());
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
624
        if (src.type()==int(QMetaType::Float   )) return tryBind(src.value<      float>());
625
        // See QT_FOR_EACH_STATIC_PRIMITIVE_TYPE in qmetatype.h
626
627
628

        // 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
629
        // QByteArray will be encoded into a QVariant to avoid ambiguities when decoding bytes
630
631
632
633

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

637
638
        if (src.isNull()) return tryNull();
        if (!src.isValid() || handleError(qBindUnexpectedValue)) return tryAny();
639
        return false;
640
    }
641

642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
    virtual bool tryBind(       QUtf8Data& r) {  QUtf8Data copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(         QString& r) {    QString copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(            bool& r) {       bool copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(           qint8& r) {      qint8 copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(          quint8& r) {     quint8 copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(          qint16& r) {     qint16 copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(         quint16& r) {    quint16 copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(          qint32& r) {     qint32 copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(         quint32& r) {    quint32 copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(          qint64& r) {     qint64 copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(         quint64& r) {    quint64 copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(           float& r) {      float copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(          double& r) {     double copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(      QByteArray& r) { QByteArray copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(        QVariant& r) {   QVariant copy(r); return tryBind(std::move(copy)); }

    virtual bool tryBind(const  QUtf8Data& r) {  QUtf8Data copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(const    QString& r) {    QString copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(const       bool& r) {       bool copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(const      qint8& r) {      qint8 copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(const     quint8& r) {     quint8 copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(const     qint16& r) {     qint16 copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(const    quint16& r) {    quint16 copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(const     qint32& r) {     qint32 copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(const    quint32& r) {    quint32 copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(const     qint64& r) {     qint64 copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(const    quint64& r) {    quint64 copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(const      float& r) {      float copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(const     double& r) {     double copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(const QByteArray& r) { QByteArray copy(r); return tryBind(std::move(copy)); }
    virtual bool tryBind(const   QVariant& r) {   QVariant copy(r); return tryBind(std::move(copy)); }
673
674

    virtual bool tryAny() { return tryNull(); }
675
676

    virtual bool handleError(QIdentifierLiteral e, QString context = QString()) { return errorHandler ? errorHandler(e, context) : true; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
677
678
};

679
680
//! Base QAbstractValue implementations with QValueMode::Read
struct QAbstractValueReader : public QAbstractValue
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
681
{
682
    virtual ~QAbstractValueReader() noexcept = default;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
683

684
    virtual QValueMode mode() const noexcept { return QValueMode::Read; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
685

686
687
688
    virtual bool tryItem(                    ) = 0;
    virtual bool tryItem(QIdentifier&       n) = 0;
    virtual bool tryItem(QIdentifierLiteral n) { QIdentifier id; return tryItem(id) && id.utf8()==n.utf8(); }
689

690
    virtual bool tryBind(   const char* u) { return tryBind(QUtf8DataView(u)); }
691
692
693
694
    virtual bool tryBind( QUtf8DataView u) { QUtf8Data r; if (tryBind(r) && r.utf8()==u.data()) return true; handleError(qBindExpectedConstant); return false; }
    virtual bool tryBind(QAsciiDataView a) { QUtf8Data r; if (tryBind(r) && r.utf8()==a.data()) return true; handleError(qBindExpectedConstant); return false; }
    virtual bool tryBind( QLatin1String l) { QString   r; if (tryBind(r) && r==l              ) return true; handleError(qBindExpectedConstant); return false; }
    virtual bool tryBind(   QStringView s) { QString   r; if (tryBind(r) && r==s              ) return true; handleError(qBindExpectedConstant); return false; }
695

696
697
    virtual bool tryBind(  QUtf8Data& s) = 0;
    virtual bool tryBind(    QString& s) { QUtf8Data u; if (!tryBind(u)) return false; s=QString::fromUtf8(u.utf8()); return true; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
698
699
700
701
    virtual bool tryBind(       bool& b) { QUtf8Data u; if (!tryBind(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                                                        { handleError(qBindExpectedBoolean); return false; } return true; }
702
703
704
705
706
707
708
709
710
711
    virtual bool tryBind(      qint8& n) {    qint64 l; if (!tryBind(l)) return false; if (l<std::numeric_limits<  qint8>::min() || std::numeric_limits<  qint8>::max()<l)      { handleError(qBindExpectedSmallerNumber); return false; } n=  qint8(l); return true; }
    virtual bool tryBind(     quint8& n) {   quint64 l; if (!tryBind(l)) return false; if (                                         std::numeric_limits< quint8>::max()<l)      { handleError(qBindExpectedSmallerNumber); return false; } n= quint8(l); return true; }
    virtual bool tryBind(     qint16& n) {    qint64 l; if (!tryBind(l)) return false; if (l<std::numeric_limits< qint16>::min() || std::numeric_limits< qint16>::max()<l)      { handleError(qBindExpectedSmallerNumber); return false; } n= qint16(l); return true; }
    virtual bool tryBind(    quint16& n) {   quint64 l; if (!tryBind(l)) return false; if (                                         std::numeric_limits<quint16>::max()<l)      { handleError(qBindExpectedSmallerNumber); return false; } n=quint16(l); return true; }
    virtual bool tryBind(     qint32& n) {    qint64 l; if (!tryBind(l)) return false; if (l<std::numeric_limits< qint32>::min() || std::numeric_limits< qint32>::max()<l)      { handleError(qBindExpectedSmallerNumber); return false; } n= qint32(l); return true; }
    virtual bool tryBind(    quint32& n) {   quint64 l; if (!tryBind(l)) return false; if (                                         std::numeric_limits<quint32>::max()<l)      { handleError(qBindExpectedSmallerNumber); return false; } n=quint32(l); return true; }
    virtual bool tryBind(     qint64& n) { QUtf8Data s; if (!tryBind(s)) return false; bool isOk=false;  qint64 v; v=s.utf8().toLongLong (&isOk); if (isOk) { n=v; return true; } handleError(qBindExpectedDecimal      ); return false; }
    virtual bool tryBind(    quint64& n) { QUtf8Data s; if (!tryBind(s)) return false; bool isOk=false; quint64 v; v=s.utf8().toULongLong(&isOk); if (isOk) { n=v; return true; } handleError(qBindExpectedDecimal      ); return false; }
    virtual bool tryBind(      float& n) { QUtf8Data s; if (!tryBind(s)) return false; bool isOk=false;   float v; v=s.utf8().toFloat    (&isOk); if (isOk) { n=v; return true; } handleError(qBindExpectedDecimal      ); return false; }
    virtual bool tryBind(     double& n) { QUtf8Data s; if (!tryBind(s)) return false; bool isOk=false;  double v; v=s.utf8().toDouble   (&isOk); if (isOk) { n=v; return true; } handleError(qBindExpectedDecimal      ); return false; }
712
713
    virtual bool tryBind( QByteArray& b) { QUtf8Data s; if (!tryBind(s)) return false; return toByteArray(b, s); }
    virtual bool tryBind( QVariant& dst) {
714
        auto suspended = setErrorHandler();
715
        quint32 size=0; QIdentifier key; QVariant item;
716
717
718
719
720
721
722
723
724
        if (trySequence(&size)) { QVariantList l; while (tryItem(   )) { l.append(              tryBind(item) ? item : QVariant()); } dst = l; return tryOut(); }
        if (tryRecord  (&size)) { QVariantMap  l; while (tryItem(key)) { l.insert(key.latin1(), tryBind(item) ? item : QVariant()); } dst = l; return tryOut(); }
        bool        b; if (tryBind( b)) { dst = QVariant(b); return true; }
        quint64     u; if (tryBind( u)) { dst = QVariant(u); return true; }
        qint64      l; if (tryBind( l)) { dst = QVariant(l); return true; }
        double      d; if (tryBind( d)) { dst = QVariant(d); return true; }
        QByteArray ba; if (tryBind(ba)) { toVariant(dst,ba); return true; }
        QUtf8Data  u8; if (tryBind(u8)) { dst = QVariant::fromValue(u8); return true; }
        QString     t; if (tryBind( t)) { dst = QVariant(t); return true; }
725
        /**/           if (tryNull(  )) { dst = QVariant::fromValue(nullptr); return true; }
726
        setErrorHandler(suspended);
727
        if (tryAny()) { dst = QVariant(); return true; }
728
        return false;
729
    }
730

731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
    virtual bool tryBind(const  QUtf8Data&  k) {  QUtf8Data r; if (tryBind(r) && k==r) return true; handleError(qBindExpectedConstant); return false; }
    virtual bool tryBind(const    QString&  k) {    QString r; if (tryBind(r) && k==r) return true; handleError(qBindExpectedConstant); return false; }
    virtual bool tryBind(const       bool&  k) {       bool r; if (tryBind(r) && k==r) return true; handleError(qBindExpectedConstant); return false; }
    virtual bool tryBind(const      qint8&  k) {      qint8 r; if (tryBind(r) && k==r) return true; handleError(qBindExpectedConstant); return false; }
    virtual bool tryBind(const     quint8&  k) {     quint8 r; if (tryBind(r) && k==r) return true; handleError(qBindExpectedConstant); return false; }
    virtual bool tryBind(const     qint16&  k) {     qint16 r; if (tryBind(r) && k==r) return true; handleError(qBindExpectedConstant); return false; }
    virtual bool tryBind(const    quint16&  k) {    quint16 r; if (tryBind(r) && k==r) return true; handleError(qBindExpectedConstant); return false; }
    virtual bool tryBind(const     qint32&  k) {     qint32 r; if (tryBind(r) && k==r) return true; handleError(qBindExpectedConstant); return false; }
    virtual bool tryBind(const    quint32&  k) {    quint32 r; if (tryBind(r) && k==r) return true; handleError(qBindExpectedConstant); return false; }
    virtual bool tryBind(const     qint64&  k) {     qint64 r; if (tryBind(r) && k==r) return true; handleError(qBindExpectedConstant); return false; }
    virtual bool tryBind(const    quint64&  k) {    quint64 r; if (tryBind(r) && k==r) return true; handleError(qBindExpectedConstant); return false; }
    virtual bool tryBind(const      float&  k) {      float r; if (tryBind(r) && k==r) return true; handleError(qBindExpectedConstant); return false; } // TODO silent warning
    virtual bool tryBind(const     double&  k) {     double r; if (tryBind(r) && k==r) return true; handleError(qBindExpectedConstant); return false; } // TODO silent warning
    virtual bool tryBind(const QByteArray&  k) { QByteArray r; if (tryBind(r) && k==r) return true; handleError(qBindExpectedConstant); return false; }
    virtual bool tryBind(const   QVariant&  k) {   QVariant r; if (tryBind(r) && k==r) return true; handleError(qBindExpectedConstant); return false; }

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

763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
    virtual bool tryAny() {
        auto suspended = setErrorHandler();
        if (  (trySequence() && tryOut())
            ||(tryRecord  () && tryOut())) {
            return true;
        }
        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;
        QVariant v;
        QByteArray bytes;
        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)
        bool bound = tryBind(b) || tryBind(d) || tryBind(i) || tryBind(u) || tryBind(v) || tryBind(bytes) || tryNull();
        setErrorHandler(suspended);
        return bound || handleError(qBindUnexpectedValue);
    }
780

781
    virtual bool handleError(QIdentifierLiteral e, QString context = QString()) { return errorHandler ? errorHandler(e, context) : false; }
782
protected:
783
784
    bool toByteArray(QByteArray& b, QUtf8Data s) {
        const QByteArray& bytes = s.utf8();
785
        if (!bytes.startsWith("0x") || s.size()%2==1) return false;
786
        b.reserve((s.size()-2)/2);
787
        auto fromHex = [](char a) -> char { return '0'<=a && a<='9' ? a-'0' : 'A'<=a && a<='F' ? a-'A'+10 : 'a'<=a && a<='f' ? a-'a'+10 : -1; };
788
        for (int i=0; i < b.size(); i++) {
789
            char highDigit=fromHex(bytes[2+2*i]), lowDigit=fromHex(bytes[2+2*i+1]);
790
791
792
793
794
795
796
            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
797
    void toVariant(QVariant& v, const QByteArray& b) {
798
799
800
801
802
803
        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
804
805
806
};

// //////////////////////////////////////////////////////////////////////////
807
// Dynamic and deferred QAbstractValueWriter support
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
808
809
810

#include <QtCore/qatomic.h>

811
class QLogger : public QCur
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
812
{
813
    static QAtomicPointer<QAbstractValueWriter> s_impl;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
814
public:
815
    static QAbstractValueWriter* setWriter(QAbstra