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

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

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

48
49
#include <QtCore/qvariant.h>

50
#include "QData.h"
51

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

55
extern QIdentifierLiteral qBindUnexpectedValue;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
56
57
58
59
60
61
62
63
64
extern QIdentifierLiteral qBindExpectedItem;
extern QIdentifierLiteral qBindExpectedNull;
extern QIdentifierLiteral qBindExpectedSequence;
extern QIdentifierLiteral qBindExpectedRecord;
extern QIdentifierLiteral qBindExpectedText;
extern QIdentifierLiteral qBindExpectedBytes;
extern QIdentifierLiteral qBindExpectedInteger;
extern QIdentifierLiteral qBindExpectedDecimal;
extern QIdentifierLiteral qBindExpectedSmallerNumber;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
65
extern QIdentifierLiteral qBindExpectedPositiveInteger;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
66
67
extern QIdentifierLiteral qBindExpectedBoolean;
extern QIdentifierLiteral qBindExpectedConstant;
68

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

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

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

#include <QtCore/qstring.h>

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

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

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

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

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

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

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

106
extern QIdentifierLiteral qmColor;
107

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

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

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

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

template<> struct BindSupport<  QUtf8Data> : BindNative {};
template<> struct BindSupport<    QString> : BindNative {};
//template<> struct BindSupport<      QChar> : BindNative {};
template<> struct BindSupport<       bool> : BindNative {};
template<> struct BindSupport<      qint8> : BindNative {};
template<> struct BindSupport<     quint8> : BindNative {};
template<> struct BindSupport<     qint16> : BindNative {};
template<> struct BindSupport<    quint16> : BindNative {};
template<> struct BindSupport<     qint32> : BindNative {};
template<> struct BindSupport<    quint32> : BindNative {};
template<> struct BindSupport<     qint64> : BindNative {};
template<> struct BindSupport<    quint64> : BindNative {};
//template<> struct BindSupport<   qfloat16> : BindNative {};
template<> struct BindSupport<      float> : BindNative {};
template<> struct BindSupport<     double> : BindNative {};
//template<> struct BindSupport<      QDate> : BindNative {};
//template<> struct BindSupport<  QDateTime> : BindNative {};
//template<> struct BindSupport<      QTime> : BindNative {};
//template<> struct BindSupport<      QUuid> : BindNative {};
template<> struct BindSupport< QByteArray> : BindNative {};
template<> struct BindSupport<   QVariant> : BindNative {};

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

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

259
    explicit QValueStatus(QAbstractValue* i) : impl(i) { Q_ASSERT(impl); }
260

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
261
    QValueMode           mode() const noexcept { return impl ? impl->mode() : QValueMode::Invalid; }
262
    explicit operator    bool() const noexcept { return impl && impl->isValid(); } //!< Drives QTransmogrifier<T>::bind() traversal
263
    QValueStatus* operator ->()       noexcept { return this; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
264
    QVal<QValueStatus>  value()       noexcept ;
265

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

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

285
    template<typename T> bool tryBind(             T&& t) { return tryBind(BindSupport<RemoveCvRef<T>>(), std::forward<T>(t)); }
286
private:
287
288
    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) ;
289

290
291
292
293
    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 ( ); }
294
private:
295
    QValueStatus _unsafeCopy() noexcept { return impl ? QValueStatus(impl) : QValueStatus(); }
296

297
    QAbstractValue* impl = nullptr;
298
};
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
299

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

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
303
304
305
306
307
308
// //////////////////////////////////////////////////////////////////////////
// Generic fluent interface for traversing structured values and processing them along the way:
// - serializing, deserializing
// - constructing generic in-memory data structures
// - etc...

309
310
template<class T_> class QRec; //!< a Record   data structure defined below
template<class T_> class QSeq; //!< a Sequence data structure defined below
311
template<class T_> class QVal; //!< a choice of sequence(), record(), null(), or values with at least a textual representation and possibly binary ones
312
313
314
315

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

317
// Custom bind support
318
319
320

#include <functional>

321
#ifndef NO_COMPILER_RTTI_OR_EXCEPTIONS
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
322
323
template<typename T> using QValFunction = QValueStatus(*)(T &,QValue   &&) ;
template<class   Ts> using QSeqFunction = QSequence   (*)(Ts&,QSequence&&) ;
324
#endif
325
326
/**/     using QValLambda = std::function<QValueStatus(QValue   &&)>;
/**/     using QSeqLambda = std::function<QSequence   (QSequence&&)>;
327

328
template<class T_> class QVal
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
329
{
330
    Q_DISABLE_COPY(QVal)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
331
public:
332
333
    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
334

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

338
    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
339

340
341
    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
342
343
    /**/ 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
344

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
345
346
347
348
349
    /**/ 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
350

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

354
    // Custom bind support
355
356
    /**/                 T_  with(      QValLambda      customBind) { return customBind(   std::move(unsafeThis())) ? std::move(outer) : T_(); }
    template<typename T> T_  with(T& t, QValFunction<T> customBind) { return customBind(t, std::move(unsafeThis())) ? std::move(outer) : T_(); }
357

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
358
    // Literal metadata support
359
360
361
362
363
    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
364

365
366
367
    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(); }
368

369
    // Shortcuts
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
370
    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
371
private:
372
    QValue unsafeThis() noexcept { return QValue(outer->_unsafeCopy()); }
373

374
    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
375
376
};

377
template<class T_> class QSeq
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
378
{
379
    Q_DISABLE_COPY(QSeq)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
380
public:
381
382
    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
383

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

388
    /**/      T_    out() { return outer-> tryOut() ?                std::move(outer)  :           T_  (); }
389
    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
390
391

    // Shortcuts
392
393
    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
394
395
    /**/ QSeq<T_>  null    (                  ) { return item().null              ( ) ; }
    /**/ QSeq<T_>  any     (                  ) { return item().any               ( ) ; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
396

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
397
398
399
400
401
    /**/ 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
402

403
404
    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)); }
405

406
    // Custom bind support
407
408
    /**/                 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
409
410
    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; });
411

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
412
    // Shortcut
413
    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
414
private:
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
415
    template<typename T, typename TEnabledIf> friend struct QTransmogrifier;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
416
417
    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
418
419
420
421

    T_ outer = T_();
};

422
template<class T_> class QRec
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
423
{
424
    Q_DISABLE_COPY(QRec)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
425
public:
426
427
    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
428

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

433
434
435
    /**/    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
436
437

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

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
442
443
444
445
446
    /**/ 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
447

448
449
    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)); }
450
451

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

    T_ outer = T_();
};

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
461
462
//! 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
463
//!
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
464
//! Its least specialized definition calls a T::bind(QValue&&) method that is more convenient to define than a QTransmogrifier specialization
465
//! \see Person::bind() definition in main.cpp for an example
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
466
//!
467
//! \remark bind is not a template function to avoid conflicts with ADL and allow more precise class specialization
468
//! \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
469
//! \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
470
//! \remark QTransmogrifier has a structure similar to a Traversable : https://wiki.haskell.org/Typeclassopedia#Traversable
471
472
//!         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
473
474
//!
template<typename T, typename TEnabledIf=void>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
475
struct QTransmogrifier {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
476
477
478
    static QValueStatus 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 QValueStatus 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 QValueStatus 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
479
480
};

481
template<class T>
482
bool QValueStatus::tryBind(BindGeneric, T&& t) { return bool(QTransmogrifier<RemoveCvRef<T>>::zap(QValue(_unsafeCopy()),std::forward<T>(t))); }
483

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

508
509
#include <QtCore/qdatastream.h>

510
511
//! Base QAbstractValue implementation with QValueMode::Write
struct QAbstractValueWriter : public QAbstractValue
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
512
{
513
    virtual ~QAbstractValueWriter() noexcept = default;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
514

515
    virtual QValueMode mode() const noexcept { return QValueMode::Write; }
516

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

519
520
521
    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
522
523

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

        // 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
599
        // QByteArray will be encoded into a QVariant to avoid ambiguities when decoding bytes
600
601
602
603

        QByteArray binaryVariant; QDataStream s(&binaryVariant, QIODevice::WriteOnly);
        s << src;
        if (s.status()==QDataStream::Ok) {
604
            return tryBind(binaryVariant);
605
606
        }

607
608
        if (src.isNull()) return tryNull();
        if (!src.isValid() || handleError(qBindUnexpectedValue)) return tryAny();
609
        return false;
610
    }
611

612
613
614
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
    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)); }
643
644

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

647
648
//! Base QAbstractValue implementations with QValueMode::Read
struct QAbstractValueReader : public QAbstractValue
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
649
{
650
    virtual ~QAbstractValueReader() noexcept = default;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
651

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

654
655
656
    virtual bool tryItem(                    ) = 0;
    virtual bool tryItem(QIdentifier&       n) = 0;
    virtual bool tryItem(QIdentifierLiteral n) { QIdentifier id; return tryItem(id) && id.utf8()==n.utf8(); }
657

658
    virtual bool tryBind(   const char* u) { return tryBind(QUtf8DataView(u)); }
659
660
661
662
    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; }
663

664
665
666
    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; }
    virtual bool tryBind(       bool& b) { QUtf8Data u; if (!tryBind(u)) return false; if (u.utf8().compare("true" , Qt::CaseInsensitive)==0) { b=true ; } else
667
668
669
670
671
672
673
674
675
676
677
                                                                                       if (u.utf8().compare("false", Qt::CaseInsensitive)==0) { b=false; } else                 { handleError(qBindExpectedBoolean      ); return false; }               return true; }
    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; }
678
679
    virtual bool tryBind( QByteArray& b) { QUtf8Data s; if (!tryBind(s)) return false; return toByteArray(b, s); }
    virtual bool tryBind( QVariant& dst) {
680
        auto suspended = setErrorHandler();
681

682
        quint32 size=0; QIdentifier key; QVariant item;
683
684
685
686
687
688
689
690
691
        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; }
692
        /**/           if (tryNull(  )) { dst = QVariant::fromValue(nullptr); return true; }
693
        setErrorHandler(suspended);
694
695
        if (tryAny() || handleError(qBindUnexpectedValue)) { dst = QVariant(); return true; }
        return false;
696
    }
697

698
699
700
701
702
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
    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; }
729

730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
    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);
    }
747
748

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

    QValueErrorHandler errorHandler = nullptr;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
774
775
776
};

// //////////////////////////////////////////////////////////////////////////
777
// Dynamic and deferred QAbstractValueWriter support
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
778
779
780

#include <QtCore/qatomic.h>

781
class QLogger : public QValueStatus
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
782
{
783
    static QAtomicPointer<QAbstractValueWriter> s_impl;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
784
public:
785
    static QAbstractValueWriter* setWriter(QAbstractValueWriter* i) { return s_impl.fetchAndStoreOrdered(i); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
786

787
    QLogger() : QValueStatus(s_impl) {}
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
788
789
};

790
// TODO in .cpp QAtomicPointer<QAbstractValueWriter> QLogger::s_impl = nullptr;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
791
792
793

#include <functional>

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
794
//! 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
795
//! \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
796
797
//! \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
798
{
799
    std::function<QValueStatus(QValue&&)> f;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
800
public:
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
801
    QBindable() {}
802
803
    template<typename T> QBindable(T&  t) : f(/*captured T ref */[&t] (QValue&& v) { return v.bind(t); } ) {}
    template<typename T> QBindable(T&& t) : f(/*captured T copy*/[ t] (QValue&& v) { return v.bind(t); } ) {}
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
804

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
805
    QValueStatus zap(QValue&& v) { return f(std::move(v)); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
806
807
808
};

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

EXT Arnaud Clère's avatar
merged  
EXT Arnaud Clère committed
811
template<class T_>
EXT Arnaud Clère's avatar
WIP  
EXT Arnaud Clère committed
812
template<class Ts>
813
814
QSeq<T_> QSeq<T_>::forEach(Ts& ts,
                         QValueStatus(*itemBind)(typename Ts::value_type&, QValue&&),
EXT Arnaud Clère's avatar