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.6 KB
Newer Older
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/****************************************************************************
 * **
 * ** Copyright (C) 2017 MinMaxMedical.
 * ** All rights reserved.
 * ** Contact: MinMaxMedical <InCAS@MinMaxMedical.com>
 * **
 * ** This file is part of the modmedLog module.
 * **
 * ** $QT_BEGIN_LICENSE:BSD$
 * ** You may use this file under the terms of the BSD license as follows:
 * **
 * ** "Redistribution and use in source and binary forms, with or without
 * ** modification, are permitted provided that the following conditions are
 * ** met:
 * **   * Redistributions of source code must retain the above copyright
 * **     notice, this list of conditions and the following disclaimer.
 * **   * Redistributions in binary form must reproduce the above copyright
 * **     notice, this list of conditions and the following disclaimer in
 * **     the documentation and/or other materials provided with the
 * **     distribution.
 * **   * Neither the name of MinMaxMedical S.A.S. and its Subsidiary(-ies) nor
 * **     the names of its contributors may be used to endorse or promote
 * **     products derived from this software without specific prior written
 * **     permission.
 * **
 * ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 * ** $QT_END_LICENSE$
 * **
 * ****************************************************************************/
#pragma once

#include <QtCore/qglobal.h>
#define Q_ENABLE_MOVE_DEFAULT(Class) \
44
45
46
    Class           (         ) noexcept = default; \
    Class           (Class&& o) noexcept = default; \
    Class& operator=(Class&& o) noexcept = default;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
47

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
149
150
//! Interface for QValueStatus with a fixed subset of BindNative types and just a few optional methods
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
struct QValueEnd { bool isValid = false; };

242
243
//! A QValueStatus is a move-only, non-owning pointer to QAbstractValue
//!
244
//! Its public interface only allows checking the status of resulting QAbstractValue operation(s) using operator bool() and to handleError().
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
245
//! Its protected interface is used by QVal, QSeq, QRec to only allow new QAbstractValue operation from the previous valid QValueStatus.
246
//!
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
247
//! It allows to easily add QTransmogrifier support to existing Reader/Writers by adding at least one value() method returning a QValueStatus(this).value()
248
//! \see TextWriter in main.cpp
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
249
//!
250
//! \remark QValueStatus allows using the fluent interface without checking intermediate status by setting impl=nullptr on error
251
//! and subsequently bypassing impl calls. Errors can result from:
252
253
//! - 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
254
//!
255
class QValueStatus
256
{
257
    Q_DISABLE_COPY(QValueStatus)
258
public:
259
260
261
    QValueStatus           (                ) noexcept : impl(nullptr) {}
    QValueStatus           (QValueStatus&& o) noexcept : impl(o.impl) { o.impl = nullptr; }
    QValueStatus& operator=(QValueStatus&& o) noexcept { auto prev = impl; impl = o.impl; o.impl = prev; return *this; }
262

263
    explicit QValueStatus(QAbstractValue* i) : impl(i) { Q_ASSERT(impl); }
264

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
265
    QValueMode           mode() const noexcept { return impl ? impl->mode() : QValueMode::Invalid; }
266
    explicit operator    bool() const noexcept { return impl && impl->isValid(); } //!< Drives QTransmogrifier<T>::bind() traversal
267
    QValueStatus* operator ->()       noexcept { return this; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
268
    QVal<QValueStatus>  value()       noexcept ;
269
    operator        QValueEnd()                { return QValueEnd{impl!=nullptr}; }
270

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
271
    QValueErrorHandler setErrorHandler(QValueErrorHandler newHandler = nullptr) { return Q_LIKELY(impl) ? impl->setErrorHandler(newHandler) : nullptr; }
272
273
    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); }
274
protected:
275
276
277
    template<class T_> friend class QVal; // enables calling methods below
    template<class T_> friend class QSeq;
    template<class T_> friend class QRec;
278
279
280

    void _meta    (QIdentifierLiteral& n,
                   QAsciiData&         m) { if    (Q_LIKELY(impl) )  impl->_meta  (n,m); } //!< idempotent (can be called by more than one code)
281
282
283
    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
284
    bool tryAny     (                     ) { return Q_LIKELY(impl) && impl->tryAny     ( ); }
285
286
287
288
    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); }
289

290
    template<typename T> bool tryBind(             T&& t) { return tryBind(BindSupport<RemoveCvRef<T>>(), std::forward<T>(t)); }
291
private:
292
293
    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) ;
294

295
296
297
298
    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 ( ); }
299
private:
300
    QValueStatus _unsafeCopy() noexcept { return impl ? QValueStatus(impl) : QValueStatus(); }
301

302
    QAbstractValue* impl = nullptr;
303
};
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
304

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

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
308
309
310
311
312
313
// //////////////////////////////////////////////////////////////////////////
// Generic fluent interface for traversing structured values and processing them along the way:
// - serializing, deserializing
// - constructing generic in-memory data structures
// - etc...

314
315
template<class T_> class QRec; //!< a Record   data structure defined below
template<class T_> class QSeq; //!< a Sequence data structure defined below
316
template<class T_> class QVal; //!< a choice of sequence(), record(), null(), or values with at least a textual representation and possibly binary ones
317
318
319
320

using QValue    = QVal<QValueStatus>;
using QSequence = QSeq<QValueStatus>;
using QRecord   = QRec<QValueStatus>;
321

322
// Custom bind support
323
324
325

#include <functional>

326
#ifndef NO_COMPILER_RTTI_OR_EXCEPTIONS
327
328
template<typename T> using QValFunction = QValueEnd(*)(T &,QValue   &&) ;
template<class   Ts> using QSeqFunction = QSequence(*)(Ts&,QSequence&&) ;
329
#endif
330
331
/**/     using QValLambda = std::function<QValueEnd(QValue   &&)>;
/**/     using QSeqLambda = std::function<QSequence(QSequence&&)>;
332

333
template<class T_> class QVal
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
334
{
335
    Q_DISABLE_COPY(QVal)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
336
public:
337
338
    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
339

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

343
    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
344

345
346
    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
347
348
    /**/ 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
349

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
350
351
352
353
354
    /**/ 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
355

356
357
    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
358

359
    // Custom bind support
360
361
    /**/                 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_(); }
362

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
363
    // Literal metadata support
364
365
366
367
368
    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
369

370
371
372
    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(); }
373

374
    // Shortcuts
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
375
    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
376
private:
377
    QValue unsafeThis() noexcept { return QValue(outer->_unsafeCopy()); }
378

379
    T_ outer = T_(); //!< moved context of current traversal up to QValueStatus that will point to the value itself (be it a QIODevice or QCborValue)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
380
381
};

382
template<class T_> class QSeq
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
383
{
384
    Q_DISABLE_COPY(QSeq)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
385
public:
386
387
    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
388

389
    explicit operator   bool() const noexcept { return outer.operator bool(); } //!< Drives QTransmogrifier<T>::bind() traversal
390
    QValueStatus* operator->()       noexcept { return outer.operator   ->(); }
391
    operator       QValueEnd()                { return out(); /* calls T_::operator QValueEnd() if T_ != QValueEnd */ }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
392

393
    /**/      T_    out() { return outer-> tryOut() ?                std::move(outer)  :           T_  (); }
394
    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
395
396

    // Shortcuts
397
398
    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
399
400
    /**/ QSeq<T_>  null    (                  ) { return item().null              ( ) ; }
    /**/ QSeq<T_>  any     (                  ) { return item().any               ( ) ; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
401

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
402
403
404
405
406
    /**/ 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
407

408
409
    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)); }
410

411
    // Custom bind support
412
413
    /**/                 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_>(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
414
415
    template<class   Ts> QSeq<T_>  forEach (Ts& ts, QValueStatus(*)(typename Ts::value_type&, QValue&&)
                                                  , bool        (*)(const typename Ts::value_type&) = [](const typename Ts::value_type&) { return true; });
416

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
417
    // Shortcut
418
    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
419
private:
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
420
    template<typename T, typename TEnabledIf> friend struct QTransmogrifier;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
421
422
    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
423
424
425
426

    T_ outer = T_();
};

427
template<class T_> class QRec
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
428
{
429
    Q_DISABLE_COPY(QRec)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
430
public:
431
432
    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
433

434
    explicit operator   bool() const noexcept { return outer.operator bool(); } //!< Drives QTransmogrifier<T>::bind() traversal
435
    QValueStatus* operator->()       noexcept { return outer.operator   ->(); }
436
    operator       QValueEnd()                { return out(); /* calls T_::operator QValueEnd() if T_ != QValueEnd */ }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
437

438
439
440
    /**/    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
441
442

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

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
447
448
449
450
451
    /**/ 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
452

453
454
    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)); }
455
456

    // Custom bind support
457
458
    /**/                 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
459
private:
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
460
    template<typename T, typename TEnabledIf> friend struct QTransmogrifier;
461
    QValue unsafeItem(QIdentifier& n) noexcept { return outer->tryItem(n) ? QValue(outer._unsafeCopy()) : QValue(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
462
463
464
465

    T_ outer = T_();
};

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
466
467
//! QTransmogrifier is a class of template methods binding T parts with QValue obeying a simple generic data model, and returning the QValueStatus
//! 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
468
//!
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
469
//! Its least specialized definition calls a T::bind(QValue&&) method that is more convenient to define than a QTransmogrifier specialization
470
//! \see Person::bind() definition in main.cpp for an example
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
471
//!
472
//! \remark bind is not a template function to avoid conflicts with ADL and allow more precise class specialization
473
//! \remark QVal<> is a move-only type passed by rvalue reference following http://scottmeyers.blogspot.com/2014/07/should-move-only-types-ever-be-passed.html
474
//! \remark bind is more explicit than operator() but could be changed to enable use as std::function
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
475
//! \remark QTransmogrifier has a structure similar to a Traversable : https://wiki.haskell.org/Typeclassopedia#Traversable
476
477
//!         A QTraverse<QValue,T> would be equivalent but would not mandate to actually traverse T to get from QValue to the actual QValueStatus
//!         A QFold<QValueStatus,T> would be equivalent to a Foldable allowing to fold T to any QValueStatus without mandating a common QVal/QSeq structure
478
479
//!
template<typename T, typename TEnabledIf=void>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
480
struct QTransmogrifier {
481
482
483
    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
484
485
};

486
template<class T>
487
bool QValueStatus::tryBind(BindGeneric, T&& t) { return QTransmogrifier<RemoveCvRef<T>>::zap(QValue(_unsafeCopy()),std::forward<T>(t)).isValid; }
488

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

513
514
#include <QtCore/qdatastream.h>

515
516
//! Base QAbstractValue implementation with QValueMode::Write
struct QAbstractValueWriter : public QAbstractValue
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
517
{
518
    virtual ~QAbstractValueWriter() noexcept = default;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
519

520
    virtual QValueMode mode() const noexcept { return QValueMode::Write; }
521

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

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

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

        // QDataStream format is the only Qt data format that is read-write and easily extensible by users
        // It is binary and includes src.typeName() for QMetaType::User types
604
        // QByteArray will be encoded into a QVariant to avoid ambiguities when decoding bytes
605
606
607
608

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

612
613
        if (src.isNull()) return tryNull();
        if (!src.isValid() || handleError(qBindUnexpectedValue)) return tryAny();
614
        return false;
615
    }
616

617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
    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)); }
648
649

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

652
653
//! Base QAbstractValue implementations with QValueMode::Read
struct QAbstractValueReader : public QAbstractValue
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
654
{
655
    virtual ~QAbstractValueReader() noexcept = default;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
656

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

659
660
661
    virtual bool tryItem(                    ) = 0;
    virtual bool tryItem(QIdentifier&       n) = 0;
    virtual bool tryItem(QIdentifierLiteral n) { QIdentifier id; return tryItem(id) && id.utf8()==n.utf8(); }
662

663
    virtual bool tryBind(   const char* u) { return tryBind(QUtf8DataView(u)); }
664
665
666
667
    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; }
668

669
670
    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
671
672
673
674
    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; }
675
676
677
678
679
680
681
682
683
684
    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; }
685
686
    virtual bool tryBind( QByteArray& b) { QUtf8Data s; if (!tryBind(s)) return false; return toByteArray(b, s); }
    virtual bool tryBind( QVariant& dst) {
687
        auto suspended = setErrorHandler();
688

689
        quint32 size=0; QIdentifier key; QVariant item;
690
691
692
693
694
695
696
697
698
        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; }
699
        /**/           if (tryNull(  )) { dst = QVariant::fromValue(nullptr); return true; }
700
        setErrorHandler(suspended);
701
702
        if (tryAny() || handleError(qBindUnexpectedValue)) { dst = QVariant(); return true; }
        return false;
703
    }
704

705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
    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; }
736

737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
    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);
    }
754
755

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

    QValueErrorHandler errorHandler = nullptr;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
781
782
783
};

// //////////////////////////////////////////////////////////////////////////
784
// Dynamic and deferred QAbstractValueWriter support
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
785
786
787

#include <QtCore/qatomic.h>

788
class QLogger : public QValueStatus
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
789
{
790
    static QAtomicPointer<QAbstractValueWriter> s_impl;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
791
public:
792
    static QAbstractValueWriter* setWriter(QAbstractValueWriter* i) { return s_impl.fetchAndStoreOrdered(i); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
793

794
    QLogger() : QValueStatus(s_impl) {}
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
795
796
};

797
// TODO in .cpp QAtomicPointer<QAbstractValueWriter> QLogger::s_impl = nullptr;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
798
799
800

#include <functional>

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
801
//! 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
802
//! \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
803
804
//! \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
805
{
806
    std::function<QValueStatus(QValue&&)> f;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
807
public:
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
808
    QBindable() {}
809
810
    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) { retu