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.2 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;
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
281
    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 ;
    operator     QValueEnd()                { return QValueEnd{impl!=nullptr}; }
282

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

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

302
    template<typename T> bool tryBind(             T&& t) { return tryBind(BindSupport<RemoveCvRef<T>>(), std::forward<T>(t)); }
303
private:
304
305
    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) ;
306

307
308
309
310
    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 ( ); }
311
private:
312
    QCur _unsafeCopy() noexcept { return impl ? QCur(impl) : QCur(); }
313

314
    QAbstractValue* impl = nullptr;
315
};
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
316

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

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

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

330
331
332
using QValue    = QVal<QCur>;
using QSequence = QSeq<QCur>;
using QRecord   = QRec<QCur>;
333

334
// Custom bind support
335
336
337

#include <functional>

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

345
template<class T_> class QVal
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
346
{
347
    Q_DISABLE_COPY(QVal)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
348
public:
349
350
    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
351

352
353
    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
354

355
    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
356

357
358
    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
359
360
    /**/ 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
361

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
362
363
364
365
366
    /**/ 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
367

368
369
    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
370

371
    // Custom bind support
372
373
    /**/                 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_(); }
374

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
375
    // Literal metadata support
376
377
378
379
380
    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
381

382
383
384
    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(); }
385

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

391
    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
392
393
};

394
template<class T_> class QSeq
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
395
{
396
    Q_DISABLE_COPY(QSeq)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
397
public:
398
399
    Q_ENABLE_MOVE_DEFAULT(QSeq)
    explicit QSeq(T_&& out) noexcept { std::swap(outer, out); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
400

401
402
403
    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
404

405
    /**/      T_    out() { return outer-> tryOut() ?                std::move(outer)  :           T_  (); }
406
    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
407
408

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

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
414
415
416
417
418
    /**/ 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
419

420
421
    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)); }
422

423
    // Custom bind support
424
425
    /**/                 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_>(); }
426
427
    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; });
428

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
429
    // Shortcut
430
    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
431
private:
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
432
    template<typename T, typename TEnabledIf> friend struct QTransmogrifier;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
433
434
    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
435
436
437
438

    T_ outer = T_();
};

439
template<class T_> class QRec
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
440
{
441
    Q_DISABLE_COPY(QRec)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
442
public:
443
444
    Q_ENABLE_MOVE_DEFAULT(QRec)
    explicit QRec(T_&& out) noexcept { std::swap(outer, out); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
445

446
447
448
    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
449

450
451
452
    /**/    T_   out (              ) { return outer->tryOut (                     ) ?              std::move(outer)  :         T_  (); }
    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
453
454

    // Shortcuts
455
456
    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
457
    /**/ QRec<T_>  null    (const char* n                    ) { return item(n).null              ( ) ; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
458

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
459
460
461
462
463
    /**/ 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
464

465
466
    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)); }
467
468

    // Custom bind support
469
470
    /**/                 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
471
private:
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
472
    template<typename T, typename TEnabledIf> friend struct QTransmogrifier;
473
    QValue unsafeItem(QIdentifier& n) noexcept { return outer->tryItem(n) ? QValue(outer._unsafeCopy()) : QValue(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
474
475
476
477

    T_ outer = T_();
};

478
//! 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
479
//! 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
480
//!
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
481
//! Its least specialized definition calls a T::bind(QValue&&) method that is more convenient to define than a QTransmogrifier specialization
482
//! \see Person::bind() definition in main.cpp for an example
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
483
//!
484
//! \remark bind is not a template function to avoid conflicts with ADL and allow more precise class specialization
485
//! \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
486
487
//!
template<typename T, typename TEnabledIf=void>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
488
struct QTransmogrifier {
489
490
491
    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
492
493
};

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

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
497
// //////////////////////////////////////////////////////////////////////////
498
// Base QAbstractValue implementations for Read and Write QValueMode
499
static char *qulltoa2(char *p, qulonglong n, int base=10) //!< Reproduced here for benchmark purposes only
500
501
502
503
504
505
506
507
508
509
{
    *--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;
}
510
static char * qlltoa2(char *p,  qlonglong n, int base=10) //!< Reproduced here for benchmark purposes only
511
512
513
514
515
516
517
518
519
520
{
    if (n < 0) {
        p = qulltoa2(p, qulonglong(-(1 + n)) + 1, base);
        *--p = '-';
    } else {
        p = qulltoa2(p, qulonglong(n), base);
    }
    return p;
}

521
522
#include <QtCore/qdatastream.h>

523
524
//! Base QAbstractValue implementation with QValueMode::Write
struct QAbstractValueWriter : public QAbstractValue
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
525
{
526
    virtual ~QAbstractValueWriter() noexcept = default;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
527

528
    virtual QValueMode mode() const noexcept { return QValueMode::Write; }
529

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

532
533
534
    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
535
536

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

        // 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
612
        // QByteArray will be encoded into a QVariant to avoid ambiguities when decoding bytes
613
614
615
616

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

620
621
        if (src.isNull()) return tryNull();
        if (!src.isValid() || handleError(qBindUnexpectedValue)) return tryAny();
622
        return false;
623
    }
624

625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
    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)); }
656
657

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

660
661
//! Base QAbstractValue implementations with QValueMode::Read
struct QAbstractValueReader : public QAbstractValue
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
662
{
663
    virtual ~QAbstractValueReader() noexcept = default;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
664

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

667
668
669
    virtual bool tryItem(                    ) = 0;
    virtual bool tryItem(QIdentifier&       n) = 0;
    virtual bool tryItem(QIdentifierLiteral n) { QIdentifier id; return tryItem(id) && id.utf8()==n.utf8(); }
670

671
    virtual bool tryBind(   const char* u) { return tryBind(QUtf8DataView(u)); }
672
673
674
675
    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; }
676

677
678
    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
679
680
681
682
    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; }
683
684
685
686
687
688
689
690
691
692
    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; }
693
694
    virtual bool tryBind( QByteArray& b) { QUtf8Data s; if (!tryBind(s)) return false; return toByteArray(b, s); }
    virtual bool tryBind( QVariant& dst) {
695
        auto suspended = setErrorHandler();
696

697
        quint32 size=0; QIdentifier key; QVariant item;
698
699
700
701
702
703
704
705
706
        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; }
707
        /**/           if (tryNull(  )) { dst = QVariant::fromValue(nullptr); return true; }
708
        setErrorHandler(suspended);
709
710
        if (tryAny() || handleError(qBindUnexpectedValue)) { dst = QVariant(); return true; }
        return false;
711
    }
712

713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
    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; }
744

745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
    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);
    }
762
763

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

    QValueErrorHandler errorHandler = nullptr;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
789
790
791
};

// //////////////////////////////////////////////////////////////////////////
792
// Dynamic and deferred QAbstractValueWriter support
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
793
794
795

#include <QtCore/qatomic.h>

796
class QLogger : public QCur
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
797
{
798
    static QAtomicPointer<QAbstractValueWriter> s_impl;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
799
public:
800
    static QAbstractValueWriter* setWriter(QAbstractValueWriter* i) { return s_impl.fetchAndStoreOrdered(i); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
801

802
    QLogger() : QCur(s_impl) {}
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
803
804
};

805
// TODO in .cpp QAtomicPointer<QAbstractValueWriter> QLogger::s_impl = nullptr;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
806
807
808

#include <functional>

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
809
//! 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
810
//! \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
811
812
//! \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
813
{
814
    QValLambda f;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
815
public:
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
816
    QBindable() {}
817
818
    template<typename T> QBindable(T&  t) : f(/*captured T ref */[&t] (QValue&& v) { return v.bind(t); } ) {}
    template<typename T> QBindable(T&& t) : f(/*captured T copy*/[ t] (QValue&& v) { return v.bind(t); } ) {}
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
819

820
    QValueEnd zap(QValue&& v) { return f(std::move(v)); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
821
822
823
};

// //////////////////////////////////////////////////////////////////////////
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
824
// QTransmogrifier partial specializations (usually not generic on v->mode() for dynamically-sized or builtin types)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
825