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

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

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

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

#include <QtCore/qstring.h>

84
// 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
85

86
extern QIdentifierLiteral qmDataStreamVersion; //!< Allows QAbstractValue support of QDataStream
87

88
// N-dimensional data structures for which specific QAbstractValueWriter may have optimized implementations
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
89
// 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
90
91

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

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

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

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

106
extern QIdentifierLiteral qmColor;
107

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
108
// //////////////////////////////////////////////////////////////////////////
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
109
// QTransmogrifier<T>
110

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

113
using QValueErrorHandler = std::function<bool(QIdentifierLiteral,QString)>;
114

115
116
struct BindGeneric {};
struct BindNative  {};
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
117
//!< Specifies whether QVal calls QAbstractValue::tryBind or QTransmogrifier<T>::bind
118
//!< \remark It would be possible to remove BindSupport by defining all QAbstractValue overloads in QVal
119
120
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
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>

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

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
152
    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
153

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

156
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
    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;
185
186
187
188
189

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

    // Overloads for const& and && T

190
191
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
    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;

222
223
    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
224
225
226
227

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

228
229
    virtual QValueErrorHandler setErrorHandler(QValueErrorHandler newHandler = nullptr) { Q_UNUSED(newHandler) return nullptr; }
    virtual bool handleError(QIdentifierLiteral name, QString context = QString()) { Q_UNUSED(name) Q_UNUSED(context) return false; }
230
231
232
233
};

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

235
template<class T_> class QVal;
236

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

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

254
//! A QCur is a cursor inside a structured value implemented as a move-only, non-owning pointer to QAbstractValue
255
//!
256
257
//! 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.
258
//!
259
//! It allows to easily add QTransmogrifier support to existing Reader/Writers by adding at least one value() method returning a QCur(this).value()
260
//! \see TextWriter in main.cpp
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
261
//!
262
263
//! \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:
264
265
//! - 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
266
//!
267
class QCur
268
{
269
    Q_DISABLE_COPY(QCur)
270
public:
271
272
273
    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; }
274

275
    explicit QCur(QAbstractValue* i) : impl(i) { Q_ASSERT(impl); }
276

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

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
287
    QValueErrorHandler setErrorHandler(QValueErrorHandler newHandler = nullptr) { return Q_LIKELY(impl) ? impl->setErrorHandler(newHandler) : nullptr; }
288
289
    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); }
290
protected:
291
292
293
    template<class T_> friend class QVal; // enables calling methods below
    template<class T_> friend class QSeq;
    template<class T_> friend class QRec;
294
295
296

    void _meta    (QIdentifierLiteral& n,
                   QAsciiData&         m) { if    (Q_LIKELY(impl) )  impl->_meta  (n,m); } //!< idempotent (can be called by more than one code)
297
298
299
    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
300
    bool tryAny     (                     ) { return Q_LIKELY(impl) && impl->tryAny     ( ); }
301
302
303
304
    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); }
305

306
    template<typename T> bool tryBind(             T&& t) { return tryBind(BindSupport<RemoveCvRef<T>>(), std::forward<T>(t)); }
307
private:
308
309
    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) ;
310

311
312
313
314
    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 ( ); }
315
private:
316
    QCur _unsafeCopy() noexcept { return impl ? QCur(impl) : QCur(); }
317

318
    QAbstractValue* impl = nullptr;
319
};
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
320

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

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
324
325
326
327
328
329
// //////////////////////////////////////////////////////////////////////////
// Generic fluent interface for traversing structured values and processing them along the way:
// - serializing, deserializing
// - constructing generic in-memory data structures
// - etc...

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

334
335
336
using QValue    = QVal<QCur>;
using QSequence = QSeq<QCur>;
using QRecord   = QRec<QCur>;
337

338
// Custom bind support
339
340
341

#include <functional>

342
#ifndef NO_COMPILER_RTTI_OR_EXCEPTIONS
343
344
template<typename T> using QValFunction = QValueEnd(*)(T &,QValue   &&) ;
template<class   Ts> using QSeqFunction = QSequence(*)(Ts&,QSequence&&) ;
345
#endif
346
347
using QValLambda = std::function<QValueEnd(QValue   &&)>;
using QSeqLambda = std::function<QSequence(QSequence&&)>;
348

349
template<class T_> class QVal
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
350
{
351
    Q_DISABLE_COPY(QVal)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
352
public:
353
354
    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
355

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

359
    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
360

361
362
    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
363
364
    /**/ 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
365

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
366
367
368
369
370
    /**/ 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
371

372
373
    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
374

375
    // Custom bind support
376
377
    /**/                 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_(); }
378

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
379
    // Literal metadata support
380
381
382
383
384
    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
385

386
387
388
    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(); }
389

390
    // Shortcuts
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
391
    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
392
private:
393
    QValue unsafeThis() noexcept { return QValue(outer->_unsafeCopy()); }
394

395
    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
396
397
};

398
template<class T_> class QSeq
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
399
{
400
    Q_DISABLE_COPY(QSeq)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
401
public:
402
    Q_ENABLE_MOVE_DEFAULT(QSeq)
403
    ~QSeq() { Q_UNUSED(QValueEnd(*this)) }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
404

405
406
407
    explicit operator bool() const noexcept { return outer.operator bool(); } //!< Drives QTransmogrifier<T>::bind() traversal
    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
408

409
    /**/      T_    out() { return outer-> tryOut() ?                std::move(outer)  :           T_  (); }
410
    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
411
412

    // Shortcuts
413
414
    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
415
416
    /**/ QSeq<T_>  null    (                  ) { return item().null              ( ) ; }
    /**/ QSeq<T_>  any     (                  ) { return item().any               ( ) ; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
417

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
418
419
420
421
422
    /**/ 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
423

424
425
    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)); }
426

427
    // Custom bind support
428
429
    /**/                 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_>(); }
430
431
    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; });
432

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
433
    // Shortcut
434
    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
435
private:
436
437
438
    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
439
    template<typename T, typename TEnabledIf> friend struct QTransmogrifier;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
440
441
    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
442
443
444
445

    T_ outer = T_();
};

446
template<class T_> class QRec
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
447
{
448
    Q_DISABLE_COPY(QRec)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
449
public:
450
    Q_ENABLE_MOVE_DEFAULT(QRec)
451
    ~QRec() { Q_UNUSED(QValueEnd(*this)) }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
452

453
454
455
    explicit operator bool() const noexcept { return outer.operator bool(); } //!< Drives QTransmogrifier<T>::bind() traversal
    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
456

457
    /**/      T_   out (              ) { return outer->tryOut (                     ) ?                std::move(outer)  :           T_  (); }
458
459
    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
460
461

    // Shortcuts
462
463
    QSeq<QRec<T_>> sequence(const char* n, quint32* s=nullptr) { return item(n).sequence          (s) ; }
    QRec<QRec<T_>> record  (const char* n, quint32* s=nullptr) { return item(n).record            (s) ; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
464
    /**/ QRec<T_>  null    (const char* n                    ) { return item(n).null              ( ) ; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
465

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
466
467
468
469
470
    /**/ QRec<T_>  bind    (const char* n,      const char* u) { return item(n).bind(QUtf8DataView(u)); }
    /**/ QRec<T_>  bind    (const char* n,    QUtf8DataView u) { return item(n).bind              (u) ; }
    /**/ QRec<T_>  bind    (const char* n,   QAsciiDataView a) { return item(n).bind              (a) ; }
    /**/ QRec<T_>  bind    (const char* n,    QLatin1String l) { return item(n).bind              (l) ; }
    /**/ QRec<T_>  bind    (const char* n,      QStringView u) { return item(n).bind              (u) ; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
471

472
473
    template<typename T> QRec<T_>  bind    (const char* n,              T&& t) { return item(n).bind(   std::forward<T>(       t)); }
    template<typename T> QRec<T_>  bind    (const char* n, T& t, T&& defaultT) { return item(n).bind(t, std::forward<T>(defaultT)); }
474
475

    // Custom bind support
476
477
    /**/                 QRec<T_>  with    (const char* n,       QValLambda      customBind) { return item(n).with(   customBind); }
    template<typename T> QRec<T_>  with    (const char* n, T& t, QValFunction<T> customBind) { return item(n).bind(t, customBind); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
478
private:
479
480
481
    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
482
    template<typename T, typename TEnabledIf> friend struct QTransmogrifier;
483
    QValue unsafeItem(QIdentifier& n) noexcept { return outer->tryItem(n) ? QValue(outer._unsafeCopy()) : QValue(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
484
485
486
487

    T_ outer = T_();
};

488
//! 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
489
//! 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
490
//!
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
491
//! Its least specialized definition calls a T::bind(QValue&&) method that is more convenient to define than a QTransmogrifier specialization
492
//! \see Person::bind() definition in main.cpp for an example
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
493
//!
494
//! \remark bind is not a template function to avoid conflicts with ADL and allow more precise class specialization
495
//! \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
496
497
//!
template<typename T, typename TEnabledIf=void>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
498
struct QTransmogrifier {
499
500
501
    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
502
503
};

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

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

531
532
#include <QtCore/qdatastream.h>

533
534
//! Base QAbstractValue implementation with QValueMode::Write
struct QAbstractValueWriter : public QAbstractValue
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
535
{
536
    virtual ~QAbstractValueWriter() noexcept = default;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
537

538
    virtual QValueMode mode() const noexcept { return QValueMode::Write; }
539

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
540
    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
541

542
543
544
    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
545
546

    //! End of sequence or record
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
    //! 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) {
571
572
573
        if (src.canConvert<QVariantList>()) {
            QSequentialIterable ts = src.value<QSequentialIterable>();
            quint32 size=quint32(ts.size());
574
            if (!trySequence(&size)) {
575
576
577
                return false;
            }
            for (QVariant t : ts) {
578
                if (!tryItem() || !tryBind(QVariant(t))) {
579
580
581
                    return false;
                }
            }
582
            return tryOut();
583
        }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
584
        if (src.canConvert<QVariantMap >()) { // TODO _meta(qmColumns,"key,value") trySequence() ...
585
586
            QAssociativeIterable ts = src.value<QAssociativeIterable>();
            quint32 size=quint32(ts.size());
587
            if (!tryRecord(&size)) {
588
589
590
591
592
                return false;
            }
            QAssociativeIterable::const_iterator i = ts.begin();
            const QAssociativeIterable::const_iterator end = ts.end();
            for ( ; i != end; ++i) {
593
                QIdentifier id(i.key().toString().toLatin1());
594
                if (!tryItem(id) || !tryBind(QVariant(*i))) {
595
596
597
                    return false;
                }
            }
598
            return tryOut();
599
        }
600
        if (src.userType() // TODO src.type if QUtf8Data becomes a Qt type
601
602
603
604
605
606
                      ==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
607
608
609
610
611
612
613
        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>());
614
615
616
        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
617
        if (src.type()==int(QMetaType::Float   )) return tryBind(src.value<      float>());
618
        // See QT_FOR_EACH_STATIC_PRIMITIVE_TYPE in qmetatype.h
619
620
621

        // 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
622
        // QByteArray will be encoded into a QVariant to avoid ambiguities when decoding bytes
623
624
625
626

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

630
631
        if (src.isNull()) return tryNull();
        if (!src.isValid() || handleError(qBindUnexpectedValue)) return tryAny();
632
        return false;
633
    }
634

635
636
637
638
639
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
    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)); }
666
667

    virtual bool tryAny() { return tryNull(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
668
669
};

670
671
//! Base QAbstractValue implementations with QValueMode::Read
struct QAbstractValueReader : public QAbstractValue
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
672
{
673
    virtual ~QAbstractValueReader() noexcept = default;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
674

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

677
678
679
    virtual bool tryItem(                    ) = 0;
    virtual bool tryItem(QIdentifier&       n) = 0;
    virtual bool tryItem(QIdentifierLiteral n) { QIdentifier id; return tryItem(id) && id.utf8()==n.utf8(); }
680

681
    virtual bool tryBind(   const char* u) { return tryBind(QUtf8DataView(u)); }
682
683
684
685
    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; }
686

687
688
    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
689
690
691
692
    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; }
693
694
695
696
697
698
699
700
701
702
    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; }
703
704
    virtual bool tryBind( QByteArray& b) { QUtf8Data s; if (!tryBind(s)) return false; return toByteArray(b, s); }
    virtual bool tryBind( QVariant& dst) {
705
        auto suspended = setErrorHandler();
706
        quint32 size=0; QIdentifier key; QVariant item;
707
708
709
710
711
712
713
714
715
        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; }
716
        /**/           if (tryNull(  )) { dst = QVariant::fromValue(nullptr); return true; }
717
        setErrorHandler(suspended);
718
        if (tryAny()) { dst = QVariant(); return true; }
719
        return false;
720
    }
721

722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
    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; }
753

754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
    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);
    }
771
772

    QValueErrorHandler setErrorHandler(QValueErrorHandler newHandler = nullptr) { auto previousHandler = errorHandler; errorHandler = newHandler; return previousHandler; }
773
    bool handleError(QIdentifierLiteral e, QString context = QString()) { return errorHandler ? errorHandler(e, context) : false; }
774
protected:
775
776
    bool toByteArray(QByteArray& b, QUtf8Data s) {
        const QByteArray& bytes = s.utf8();
777
        if (!bytes.startsWith("0x") || s.size()%2==1) return false;
778
        b.reserve((s.size()-2)/2);
779
        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; };
780
        for (int i=0; i < b.size(); i++) {
781
            char highDigit=fromHex(bytes[2+2*i]), lowDigit=fromHex(bytes[2+2*i+1]);
782
783
784
785
786
787
788
            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
789
    void toVariant(QVariant& v, const QByteArray& b) {
790
791
792
793
794
795
        QDataStream s(b); s >> v;
        if (s.status()==QDataStream::Ok) {
            return;
        }
        v = QVariant(b);
    }
796
797

    QValueErrorHandler errorHandler = nullptr;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
798
799
800
};

// //////////////////////////////////////////////////////////////////////////
801
// Dynamic and deferred QAbstractValueWriter support
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
802
803
804

#include <QtCore/qatomic.h>

805
class QLogger : public QCur
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
806
{
807
    static QAtomicPointer<QAbstractValueWriter> s_impl;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
808
public:
809
    static QAbstractValueWriter* setWriter(QAbstractValueWriter* i) { return s_impl.fetchAndStoreOrdered(i); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
810

811
    QLogger() : QCur(s_impl) {}
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
812
813
};

814
// TODO in .cpp QAtomicPointer<QAbstractValueWriter> QLogger::s_impl = nullptr;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
815
816
817

#include <functional>

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
818
//! A QTransmogrifier<T> 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
819
//! \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
820
821
//! \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
class QBindable
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
822
{
823
    QValLambda f;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
824
public:
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
825
    QBindable() {}
826
827
    template