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

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

48
49
#include <QtCore/qvariant.h>

50
#include "QData.h"
51

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

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

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

72
73
extern QIdentifierLiteral qBindUnexpectedItem;
extern QIdentifierLiteral qBindUnexpectedData;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
74
75
76
77

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

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

#include <QtCore/qstring.h>

85
// meta(QIdentifierLiteral name, QAsciiData value) is optional meta-data about current data to enable optimized processing (e.g. tag, , style, etc.) and/or transmission (QAbstractItemModel headers, CBOR tags, XML attribute, CSS, numpy.ndarray shapes, etc.)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
86

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

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

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

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

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

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

107
extern QIdentifierLiteral qmColor;
108

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

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

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
114
using QValueErrorFilter = std::function<bool(QIdentifierLiteral,QVariant)>;
115

116
117
118
119
120
#include <QtCore/qvariant.h>
//#include <QtCore/qfloat16.h>
//#include <QtCore/qdatetime.h>
//#include <QtCore/quuid.h>

121
//! Interface for QValue implementations with a fixed subset of BindNative types and just a few optional methods
122
123
struct QAbstractValue {
    virtual ~QAbstractValue() = default;
124

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

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

129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
    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;
158
159
160
161
162

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

    // Overloads for const& and && T

163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
    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;

195
196
    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
197
198
199
200

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

201
202
203
    virtual QValueErrorFilter setErrorFilter(QValueErrorFilter newFilter) {
        auto previousHandler = errorFilter;
        errorFilter = newFilter;
204
205
        return previousHandler;
    }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
206
207
    virtual bool isErrorFiltered(QIdentifierLiteral e, QVariant context = QVariant()) const { auto c = this->context(); return errorFilter && errorFilter(e, c.isNull() ? context : context.isNull() ? c : QVariantList({context, this->context()})); }
    virtual QVariant context() const { return QVariant(); }
208
protected:
209
    QValueErrorFilter errorFilter = nullptr;
210
211
212
213
};

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

215
template<class T_> class QVal;
216

217
218
219
#include <type_traits>
template<class T> using RemoveCvRef = typename std::remove_cv<typename std::remove_reference<T>::type>::type;

220
//! A QValueEnd represents the end of a QValue traversal and processing
221
//!
222
//! \warning isValid does not take into account intermediate errors that are handled by QValueErrorFilter
223
//!
224
225
226
227
228
229
230
231
232
class QValueEnd {
public:
    QValueEnd() = default;
    QValueEnd(bool result) : status(result) {}
    bool isValid() { return status; }
    explicit operator bool() { return isValid(); }
private:
    bool status = false;
};
233

234
//! A QCur is a cursor inside a structured value implemented as a move-only, non-owning pointer to QAbstractValue
235
//!
236
//! Its public interface only allows to check the status of QAbstractValue calls using explicit operator bool(), and to isErrorFiltered().
237
//! Its protected interface is used by QVal, QSeq, QRec to only allow QAbstractValue calls from the previous valid QCur.
238
//!
239
//! It allows to easily add QTransmogrifier support to existing Reader/Writers by adding at least one value() method returning a QCur(this).value()
240
//! \see TextWriter in main.cpp
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
241
//!
242
243
//! \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:
244
245
//! - 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
246
//!
247
class QCur
248
{
249
    Q_DISABLE_COPY(QCur)
250
public:
251
252
253
    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; }
254

255
    explicit QCur(QAbstractValue* i) : impl(i) { Q_ASSERT(impl); }
256

257
    QValueMode        mode() const noexcept { return impl ? impl->mode() : QValueMode::Invalid; }
258
259
    explicit operator bool() const noexcept { return isValid(); }
    bool           isValid() const noexcept { return impl && impl->isValid(); } //!< Drives QTransmogrifier<T>::bind() traversal
260
    QCur*       operator->()       noexcept { return this; }
261
    QVal<QCur>       value()       noexcept ;
262
    operator QValueEnd() {
263
        bool isValid = impl && impl->isValid();
264
        impl = nullptr;
265
266
        return QValueEnd{isValid};
    }
267

268
269
270
    QValueErrorFilter setErrorFilter(QValueErrorFilter newFilter) { return Q_LIKELY(impl) ? impl->setErrorFilter(newFilter) : newFilter; }
    bool isErrorFiltered(QIdentifierLiteral name, QString context = QString()) const { return Q_LIKELY(impl) && impl->isErrorFiltered(name, context); }
    bool isErrorFiltered(const char*   asciiName, QString context = QString()) const { return isErrorFiltered(QIdentifierLiteral(asciiName), context); }
271
protected:
272
273
274
    template<class T_> friend class QVal; // enables calling methods below
    template<class T_> friend class QSeq;
    template<class T_> friend class QRec;
275

276
    void _meta      (QIdentifierLiteral& n,
277
278
                     QAsciiData&         m) { if    (Q_LIKELY(impl) )   impl->_meta(n,m); } //!< idempotent (can be called by more than one code)
    bool tryAny     (                     ) { return Q_LIKELY(impl) &&  impl->tryAny(); }
279
280
281
    bool trySequence(   quint32* s=nullptr) { return Q_LIKELY(impl) && (impl->trySequence(s) || (isErrorFiltered(qBindExpectedSequence) && impl->tryAny())); }
    bool tryRecord  (   quint32* s=nullptr) { return Q_LIKELY(impl) && (impl->tryRecord  (s) || (isErrorFiltered(qBindExpectedRecord  ) && impl->tryAny())); }
    bool tryNull    (                     ) { return Q_LIKELY(impl) && (impl->tryNull    ( ) || (isErrorFiltered(qBindExpectedNull    ) && impl->tryAny())); }
282

283
284
    bool tryBind(        QUtf8Data&  t) { return Q_LIKELY(impl) && (impl->tryBind(t) || (isErrorFiltered(qBindExpectedText           ) && impl->tryAny())); }
    bool tryBind(          QString&  t) { return Q_LIKELY(impl) && (impl->tryBind(t) || (isErrorFiltered(qBindExpectedText           ) && impl->tryAny())); }
285
286
287

// TODO qBindExpectedConstant after successful tryBind without attempting (but testing several constants requires memoizing the bound value anyway...)

288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
    bool tryBind(const   QUtf8Data&  t) { return Q_LIKELY(impl) && (impl->tryBind(t) || (isErrorFiltered(qBindExpectedText           ) && impl->tryAny())); }
    bool tryBind(const     QString&  t) { return Q_LIKELY(impl) && (impl->tryBind(t) || (isErrorFiltered(qBindExpectedText           ) && impl->tryAny())); }
    bool tryBind(        QUtf8Data&& t) { return Q_LIKELY(impl) && (impl->tryBind(t) || (isErrorFiltered(qBindExpectedText           ) && impl->tryAny())); }
    bool tryBind(          QString&& t) { return Q_LIKELY(impl) && (impl->tryBind(t) || (isErrorFiltered(qBindExpectedText           ) && impl->tryAny())); }

    bool tryBind(        const char* t) { return Q_LIKELY(impl) && (impl->tryBind(t) || (isErrorFiltered(qBindExpectedText           ) && impl->tryAny())); }
    bool tryBind(      QUtf8DataView t) { return Q_LIKELY(impl) && (impl->tryBind(t) || (isErrorFiltered(qBindExpectedText           ) && impl->tryAny())); }
    bool tryBind(     QAsciiDataView t) { return Q_LIKELY(impl) && (impl->tryBind(t) || (isErrorFiltered(qBindExpectedText           ) && impl->tryAny())); }
    bool tryBind(      QLatin1String t) { return Q_LIKELY(impl) && (impl->tryBind(t) || (isErrorFiltered(qBindExpectedText           ) && impl->tryAny())); }
    bool tryBind(        QStringView t) { return Q_LIKELY(impl) && (impl->tryBind(t) || (isErrorFiltered(qBindExpectedText           ) && impl->tryAny())); }

    bool tryBind(             bool&  b) { return Q_LIKELY(impl) && (impl->tryBind(b) || (isErrorFiltered(qBindExpectedBoolean        ) && impl->tryAny())); }
    bool tryBind(const        bool&  b) { return Q_LIKELY(impl) && (impl->tryBind(b) || (isErrorFiltered(qBindExpectedBoolean        ) && impl->tryAny())); }
    bool tryBind(             bool&& b) { return Q_LIKELY(impl) && (impl->tryBind(b) || (isErrorFiltered(qBindExpectedBoolean        ) && impl->tryAny())); }

    bool tryBind(           quint8&  n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedPositiveInteger) && impl->tryAny())); }
    bool tryBind(          quint16&  n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedPositiveInteger) && impl->tryAny())); }
    bool tryBind(          quint32&  n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedPositiveInteger) && impl->tryAny())); }
    bool tryBind(          quint64&  n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedPositiveInteger) && impl->tryAny())); }
    bool tryBind(const      quint8&  n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedPositiveInteger) && impl->tryAny())); }
    bool tryBind(const     quint16&  n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedPositiveInteger) && impl->tryAny())); }
    bool tryBind(const     quint32&  n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedPositiveInteger) && impl->tryAny())); }
    bool tryBind(const     quint64&  n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedPositiveInteger) && impl->tryAny())); }
    bool tryBind(           quint8&& n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedPositiveInteger) && impl->tryAny())); }
    bool tryBind(          quint16&& n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedPositiveInteger) && impl->tryAny())); }
    bool tryBind(          quint32&& n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedPositiveInteger) && impl->tryAny())); }
    bool tryBind(          quint64&& n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedPositiveInteger) && impl->tryAny())); }

    bool tryBind(            qint8&  n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedInteger        ) && impl->tryAny())); }
    bool tryBind(           qint16&  n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedInteger        ) && impl->tryAny())); }
    bool tryBind(           qint32&  n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedInteger        ) && impl->tryAny())); }
    bool tryBind(           qint64&  n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedInteger        ) && impl->tryAny())); }
    bool tryBind(const       qint8&  n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedInteger        ) && impl->tryAny())); }
    bool tryBind(const      qint16&  n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedInteger        ) && impl->tryAny())); }
    bool tryBind(const      qint32&  n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedInteger        ) && impl->tryAny())); }
    bool tryBind(const      qint64&  n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedInteger        ) && impl->tryAny())); }
    bool tryBind(            qint8&& n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedInteger        ) && impl->tryAny())); }
    bool tryBind(           qint16&& n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedInteger        ) && impl->tryAny())); }
    bool tryBind(           qint32&& n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedInteger        ) && impl->tryAny())); }
    bool tryBind(           qint64&& n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedInteger        ) && impl->tryAny())); }

    bool tryBind(            float&  n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedDecimal        ) && impl->tryAny())); }
    bool tryBind(           double&  n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedDecimal        ) && impl->tryAny())); }
    bool tryBind(const       float&  n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedDecimal        ) && impl->tryAny())); }
    bool tryBind(const      double&  n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedDecimal        ) && impl->tryAny())); }
    bool tryBind(            float&& n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedDecimal        ) && impl->tryAny())); }
    bool tryBind(           double&& n) { return Q_LIKELY(impl) && (impl->tryBind(n) || (isErrorFiltered(qBindExpectedDecimal        ) && impl->tryAny())); }

    bool tryBind(       QByteArray&  v) { return Q_LIKELY(impl) && (impl->tryBind(v) || (isErrorFiltered(qBindExpectedBytes          ) && impl->tryAny())); }
    bool tryBind(const  QByteArray&  v) { return Q_LIKELY(impl) && (impl->tryBind(v) || (isErrorFiltered(qBindExpectedBytes          ) && impl->tryAny())); }
    bool tryBind(       QByteArray&& v) { return Q_LIKELY(impl) && (impl->tryBind(v) || (isErrorFiltered(qBindExpectedBytes          ) && impl->tryAny())); }

    bool tryBind(         QVariant&  v) { return Q_LIKELY(impl) && (impl->tryBind(v) || (isErrorFiltered(qBindUnexpectedValue        ) && impl->tryAny())); }
    bool tryBind(const    QVariant&  v) { return Q_LIKELY(impl) && (impl->tryBind(v) || (isErrorFiltered(qBindUnexpectedValue        ) && impl->tryAny())); }
    bool tryBind(         QVariant&& v) { return Q_LIKELY(impl) && (impl->tryBind(v) || (isErrorFiltered(qBindUnexpectedValue        ) && impl->tryAny())); }
343
344
345
346
347
348
349

    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 ( ); }

    template<typename T> bool tryBind(T&& t);
350
private:
351
352
353
    template<class T> friend class QVal;
    template<class T> friend class QSeq;
    template<class T> friend class QRec;
354
    void stop() noexcept { Q_UNUSED(isErrorFiltered(qBindStopped)); impl = nullptr; }
355

356
    QCur _unsafeCopy() noexcept { return impl ? QCur(impl) : QCur(); } // FIXME replace with std::move(outer) to avoid resuming read/write when QCur was actually stopped!
357

358
    QAbstractValue* impl = nullptr;
359
};
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
360

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

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
364
365
366
367
368
369
// //////////////////////////////////////////////////////////////////////////
// Generic fluent interface for traversing structured values and processing them along the way:
// - serializing, deserializing
// - constructing generic in-memory data structures
// - etc...

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

374
375
376
using QValue    = QVal<QCur>;
using QSequence = QSeq<QCur>;
using QRecord   = QRec<QCur>;
377

378
// Custom bind support
379
380
381

#include <functional>

382
#ifndef NO_COMPILER_RTTI_OR_EXCEPTIONS
383
384
template<typename T> using QValFunction = QValueEnd(*)(T &,QValue   &&) ;
template<class   Ts> using QSeqFunction = QSequence(*)(Ts&,QSequence&&) ;
385
#endif
386
387
using QValLambda = std::function<QValueEnd(QValue   &&)>;
using QSeqLambda = std::function<QSequence(QSequence&&)>;
388

389
390
391

struct QSuspendedValueErrorHandler
{
392
    QSuspendedValueErrorHandler(QAbstractValue* v) : suspended(v->setErrorFilter(nullptr)), resume([v,this](){ v->setErrorFilter(this->suspended); }) {}
393
394
   ~QSuspendedValueErrorHandler() { resume(); }
private:
395
    QValueErrorFilter suspended;
396
397
398
    std::function<void(void)> resume;
};

399
template<class T_> class QVal
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
400
{
401
    Q_DISABLE_COPY(QVal)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
402
public:
403
    Q_ENABLE_MOVE_DEFAULT(QVal)
404
    ~QVal() {
405
406
        if (isValid() &&
            resumeErrorHandler() &&
407
            outer->isErrorFiltered(qBindUnexpectedValue) &&
408
409
            !outer->tryAny()) {
            outer->stop(); // do not attempt outer.out()
410
411
412
        }
    }
    explicit QVal(T_&& out) noexcept { outer = std::move(out); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
413

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

418
419
    void setIsVariant() { suspendErrorHandler(); }

420
    operator QValueEnd() { return outer->isErrorFiltered(qBindUnexpectedValue) ? any() : T_(); /* calls T_::operator QValueEnd() if T_ != QValueEnd */ }
421
    QValueEnd      end() { return any(); /* calls T_::operator QValueEnd() if T_ != QValueEnd */ }
422
    /**/ T_        any() { return outer->tryAny() && resumeErrorHandler() ? std::move(outer) : T_(); }
423

424
    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
425

426
427
428
    QSeq<T_> sequence(quint32* s=nullptr) { return outer->trySequence          (s)  && resumeErrorHandler() ? QSeq<T_>(std::move(outer)) : QSeq<T_>(); }
    QRec<T_> record  (quint32* s=nullptr) { return outer->tryRecord            (s)  && resumeErrorHandler() ? QRec<T_>(std::move(outer)) : QRec<T_>(); }
    /**/ T_  null    (                  ) { return outer->tryNull              ( )  && resumeErrorHandler() ?          std::move(outer)  :      T_ (); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
429

430
431
432
433
434
    /**/ T_  bind    (     const char* u) { return outer->tryBind(QUtf8DataView(u)) && resumeErrorHandler() ?          std::move(outer)  :      T_ (); }
    /**/ T_  bind    (   QUtf8DataView u) { return outer->tryBind              (u)  && resumeErrorHandler() ?          std::move(outer)  :      T_ (); }
    /**/ T_  bind    (  QAsciiDataView a) { return outer->tryBind              (a)  && resumeErrorHandler() ?          std::move(outer)  :      T_ (); }
    /**/ T_  bind    (   QLatin1String l) { return outer->tryBind              (l)  && resumeErrorHandler() ?          std::move(outer)  :      T_ (); }
    /**/ T_  bind    (     QStringView u) { return outer->tryBind              (u)  && resumeErrorHandler() ?          std::move(outer)  :      T_ (); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
435

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

439
    // Custom bind support
440
441
    /**/                 T_  with(      QValLambda      customBind) { return customBind(   std::move(unsafeThis())).isValid() && resumeErrorHandler() ? std::move(outer) : T_(); }
    template<typename T> T_  with(T& t, QValFunction<T> customBind) { return customBind(t, std::move(unsafeThis())).isValid() && resumeErrorHandler() ? std::move(outer) : T_(); }
442

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
443
    // Literal metadata support
444
445
446
447
448
    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
449

450
451
452
    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(); }
453

454
    // Shortcuts
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
455
    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
456
private:
457
    QValue unsafeThis() noexcept { return QValue(outer->_unsafeCopy()); }
458

459
460
461
462
463
    void suspendErrorHandler() {
        if (isValid()) {
            if (suspended) {
                qWarning("Already suspended");
            }
464
            suspended = outer->setErrorFilter(nullptr);
465
466
467
468
469
        }
    }
    bool resumeErrorHandler() {
        Q_ASSERT_X(isValid(), Q_FUNC_INFO, "Failed to call resumeErrorHandler before std::move(outer)");
        if (suspended) {
470
471
            if (outer->setErrorFilter(suspended) != nullptr) {
                qWarning("QValueErrorFilter set while suspended is lost");
472
473
474
475
476
477
            }
            suspended = nullptr;
        }
        return true; // just to call from boolean expressions
    }

478
    QValueErrorFilter suspended = nullptr;
479
    T_ outer = T_(); //!< moved context of current traversal up to QCur that will point to the value itself (be it a QIODevice or QCborValue, ...)
480
481

    static bool ignoreError(QIdentifierLiteral,QString){ return true; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
482
483
};

484
template<class T_> class QSeq
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
485
{
486
    Q_DISABLE_COPY(QSeq)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
487
public:
488
    Q_ENABLE_MOVE_DEFAULT(QSeq)
489
    ~QSeq() {
490
        if (isValid() &&
491
            outer->isErrorFiltered(qBindUnexpectedEnd) &&
492
493
            !outer->tryOut()) {
            outer->stop();
494
495
        }
    }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
496

497
    explicit operator bool() const noexcept { return isValid(); }
498
    bool           isValid() const noexcept { return outer.isValid(); } //!< Drives QTransmogrifier<T>::bind() traversal
499
500
501
502
503
    QCur*       operator->()       noexcept { return outer.operator->(); }

    operator  QValueEnd() { return end(); }
    QValueEnd       end() { return out(); /* calls T_::operator QValueEnd() if T_ != QValueEnd */ }
    T_              out() { return outer->tryOut() ? std::move(outer) : T_(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
504

505
    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
506
507

    // Shortcuts
508
509
    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
510
511
    /**/ QSeq<T_>  null    (                  ) { return item().null              ( ) ; }
    /**/ QSeq<T_>  any     (                  ) { return item().any               ( ) ; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
512

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
513
514
515
516
517
    /**/ 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
518

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

522
    // Custom bind support
523
524
    /**/                 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_>(); }
525
526
    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; });
527

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
528
    // Shortcut
529
    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
530
private:
531
532
    template<class T> friend class QVal;
    explicit QSeq(T_&& out) noexcept { outer = std::move(out); }
533

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
534
    template<typename T, typename TEnabledIf> friend struct QTransmogrifier;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
535
536
    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
537
538
539
540

    T_ outer = T_();
};

541
template<class T_> class QRec
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
542
{
543
    Q_DISABLE_COPY(QRec)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
544
public:
545
    Q_ENABLE_MOVE_DEFAULT(QRec)
546
    ~QRec() {
547
        if (isValid() &&
548
            outer->isErrorFiltered(qBindUnexpectedEnd) &&
549
550
            !outer->tryOut()) {
            outer->stop();
551
552
        }
    }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
553

554
    explicit operator bool() const noexcept { return isValid(); }
555
    bool           isValid() const noexcept { return outer.isValid(); } //!< Drives QTransmogrifier<T>::bind() traversal
556
557
558
559
560
    QCur*       operator->()       noexcept { return outer.operator->(); }

    operator QValueEnd() { return end(); }
    QValueEnd      end() { return out(); /* calls T_::operator QValueEnd() if T_ != QValueEnd */ }
    T_             out() { return outer->tryOut() ? std::move(outer) : T_(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
561

562
563
    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
564
565

    // Shortcuts
566
567
568
569
570
571
572
573
    QSeq<QRec<T_>> sequence(const char* n, quint32* s=nullptr) { auto i = item(n); if (i) { return i.sequence          (s) ; } Q_UNUSED(outer->isErrorFiltered(qBindUnexpectedItem, n)); return QSeq<QRec<T_>>(); }
    QRec<QRec<T_>> record  (const char* n, quint32* s=nullptr) { auto i = item(n); if (i) { return i.record            (s) ; } Q_UNUSED(outer->isErrorFiltered(qBindUnexpectedItem, n)); return QRec<QRec<T_>>(); }
    /**/ QRec<T_>  null    (const char* n                    ) { auto i = item(n); if (i) { return i.null              ( ) ; } Q_UNUSED(outer->isErrorFiltered(qBindUnexpectedItem, n)); return      QRec<T_> (); }
    /**/ QRec<T_>  bind    (const char* n,      const char* u) { auto i = item(n); if (i) { return i.bind(QUtf8DataView(u)); } Q_UNUSED(outer->isErrorFiltered(qBindUnexpectedItem, n)); return      QRec<T_> (); }
    /**/ QRec<T_>  bind    (const char* n,    QUtf8DataView u) { auto i = item(n); if (i) { return i.bind              (u) ; } Q_UNUSED(outer->isErrorFiltered(qBindUnexpectedItem, n)); return      QRec<T_> (); }
    /**/ QRec<T_>  bind    (const char* n,   QAsciiDataView a) { auto i = item(n); if (i) { return i.bind              (a) ; } Q_UNUSED(outer->isErrorFiltered(qBindUnexpectedItem, n)); return      QRec<T_> (); }
    /**/ QRec<T_>  bind    (const char* n,    QLatin1String l) { auto i = item(n); if (i) { return i.bind              (l) ; } Q_UNUSED(outer->isErrorFiltered(qBindUnexpectedItem, n)); return      QRec<T_> (); }
    /**/ QRec<T_>  bind    (const char* n,      QStringView u) { auto i = item(n); if (i) { return i.bind              (u) ; } Q_UNUSED(outer->isErrorFiltered(qBindUnexpectedItem, n)); return      QRec<T_> (); }
574

575
576
577
578
579
580
    template<typename T> QRec<T_>  bind    (const char* n,              T&& t) {
        auto i = item(n);
        if (i) {
            auto r = i.bind(   std::forward<T>(       t));
            return r;
        }
581
        Q_UNUSED(outer->isErrorFiltered(qBindUnexpectedItem, n));
582
583
        return QRec<T_>();
    }
584
    template<typename T> QRec<T_>  bind    (const char* n, T& t, T&& defaultT) { auto i = item(n); if (i) { return i.bind(t, std::forward<T>(defaultT)); } Q_UNUSED(outer->isErrorFiltered(qBindUnexpectedItem, n)); return QRec<T_>(); }
585
586

    // Custom bind support
587
588
    /**/                 QRec<T_>  with    (const char* n,       QValLambda      customBind) { auto i = item(n); if (i) { return i.with(   customBind); } Q_UNUSED(outer->isErrorFiltered(qBindUnexpectedItem, n)); return QRec<T_>(); }
    template<typename T> QRec<T_>  with    (const char* n, T& t, QValFunction<T> customBind) { auto i = item(n); if (i) { return i.bind(t, customBind); } Q_UNUSED(outer->isErrorFiltered(qBindUnexpectedItem, n)); return QRec<T_>(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
589
private:
590
591
    template<class T> friend class QVal;
    explicit QRec(T_&& out) noexcept { outer = std::move(out); }
592

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
593
    template<typename T, typename TEnabledIf> friend struct QTransmogrifier;
594
    QValue unsafeItem(QIdentifier& n) noexcept { return outer->tryItem(n) ? QValue(outer._unsafeCopy()) : QValue(); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
595
596
597
598

    T_ outer = T_();
};

599
//! 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
600
//! 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
601
//!
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
602
//! Its least specialized definition calls a T::bind(QValue&&) method that is more convenient to define than a QTransmogrifier specialization
603
//! \see Person::bind() definition in main.cpp for an example
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
604
//!
605
//! \remark bind is not a template function to avoid conflicts with ADL and allow more precise class specialization
606
//! \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
607
608
//!
template<typename T, typename TEnabledIf=void>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
609
struct QTransmogrifier {
610
611
612
    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
613
614
};

615
template<class T>
616
bool QCur::tryBind(T&& t) {
617
618
    return QTransmogrifier<RemoveCvRef<T>>::zap(QValue(_unsafeCopy()),std::forward<T>(t)).isValid();
}
619

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
620
// //////////////////////////////////////////////////////////////////////////
621
// Base QAbstractValue implementations for Read and Write QValueMode
622
static char *qulltoa2(char *p, qulonglong n, int base=10) //!< Reproduced here for benchmark purposes only
623
624
625
626
627
628
629
630
631
632
{
    *--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;
}
633
static char * qlltoa2(char *p,  qlonglong n, int base=10) //!< Reproduced here for benchmark purposes only
634
635
636
637
638
639
640
641
642
643
{
    if (n < 0) {
        p = qulltoa2(p, qulonglong(-(1 + n)) + 1, base);
        *--p = '-';
    } else {
        p = qulltoa2(p, qulonglong(n), base);
    }
    return p;
}

644
645
#include <QtCore/qdatastream.h>

646
647
//! Base QAbstractValue implementation with QValueMode::Write
struct QAbstractValueWriter : public QAbstractValue
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
648
{
649
    virtual ~QAbstractValueWriter() noexcept = default;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
650

651
    virtual QValueMode mode() const noexcept { return QValueMode::Write; }
652

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

655
656
657
    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
658
659

    //! End of sequence or record
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
    //! 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) {
684
685
686
        if (src.canConvert<QVariantList>()) {
            QSequentialIterable ts = src.value<QSequentialIterable>();
            quint32 size=quint32(ts.size());
687
            if (!trySequence(&size)) {
688
689
690
                return false;
            }
            for (QVariant t : ts) {
691
                if (!tryItem() || !tryBind(QVariant(t))) {
692
693
694
                    return false;
                }
            }
695
            return tryOut();
696
        }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
697
        if (src.canConvert<QVariantMap >()) { // TODO _meta(qmColumns,"key,value") trySequence() ...
698
699
            QAssociativeIterable ts = src.value<QAssociativeIterable>();
            quint32 size=quint32(ts.size());
700
            if (!tryRecord(&size)) {
701
702
703
704
705
                return false;
            }
            QAssociativeIterable::const_iterator i = ts.begin();
            const QAssociativeIterable::const_iterator end = ts.end();
            for ( ; i != end; ++i) {
706
                QIdentifier id(i.key().toString().toLatin1());
707
                if (!tryItem(id) || !tryBind(QVariant(*i))) {
708
709
710
                    return false;
                }
            }
711
            return tryOut();
712
        }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
713
        if (src.userType()
714
715
716
717
718
719
                      ==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
720
721
722
723
724
725
726
        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>());
727
728
729
        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
730
        if (src.type()==int(QMetaType::Float   )) return tryBind(src.value<      float>());
731
        // See QT_FOR_EACH_STATIC_PRIMITIVE_TYPE in qmetatype.h
732
733
734

        // 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
735
        // QByteArray will be encoded into a QVariant to avoid ambiguities when decoding bytes
736
737
738
739

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

743
        if (src.isNull()) return tryNull();
744
        return isErrorFiltered(qBindUnexpectedValue) && tryAny();
745
    }
746

747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
    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)); }
778

779
    virtual bool tryAny() { return false; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
780
781
};

782
783
//! Base QAbstractValue implementations with QValueMode::Read
struct QAbstractValueReader : public QAbstractValue
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
784
{
785
    virtual ~QAbstractValueReader() noexcept = default;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
786

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

789
790
791
    virtual bool tryItem(                    ) = 0;
    virtual bool tryItem(QIdentifier&       n) = 0;
    virtual bool tryItem(QIdentifierLiteral n) { QIdentifier id; return tryItem(id) && id.utf8()==n.utf8(); }
792

793
    virtual bool tryBind(   const char* u) { return tryBind(QUtf8DataView(u)); }
794
795
796
797
    virtual bool tryBind( QUtf8DataView u) { QUtf8Data r; if (tryBind(r) && r.utf8()==u.data()) return true; isErrorFiltered(qBindExpectedConstant); return false; }
    virtual bool tryBind(QAsciiDataView a) { QUtf8Data r; if (tryBind(r) && r.utf8()==a.data()) return true; isErrorFiltered(qBindExpectedConstant); return false; }
    virtual bool tryBind( QLatin1String l) { QString   r; if (tryBind(r) && r==l              ) return true; isErrorFiltered(qBindExpectedConstant); return false; }
    virtual bool tryBind(   QStringView s) { QString   r; if (tryBind(r) && r==s              ) return true; isErrorFiltered(qBindExpectedConstant); return false; }
798

799
800
    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
801
802
803
    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; }
804