main.cpp 49 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
/****************************************************************************
 * **
 * ** Copyright (C) 2016 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$
 * **
 * ****************************************************************************/

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
41 42 43
// The core of QTransmogrifier proof-of-concept including:
// - a QValue fluent interface for describing logical data structures, providing auto completion and well-formedness guarantees
// - a set of recursive QTransmogrifier<T> functors binding QAbstractValue and T according to their logical data structure
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
44

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
45
#include "QValue.h"
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
46

47
// Extensible set of QAbstractValue implementations providing concrete syntax to the logical data structure in a specific Mode among Write, Read, ...
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
48

49
#include "QJson_impl.h"     // QJsonWriter, QJsonReader, QJsonBuilder, QJsonVisitor and QTransmogrifier<QJsonValue> support
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
50 51
#include "QCbor_impl.h"     // QCborWriter demonstrating the performance potential of the approach
#include "QData_impl.h"     // QDataReader, QDataWriter demonstrating a QDataStream with implicit types for benchmarking
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
52
#include "QVariant_impl.h"  // QVariantBuilder, QVariantVisitor and QTransmogrifier<QVariant,_> support
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
53
#include "QModel_impl.h"    // Q*ModelWriter support
54
#include "QXml_impl.h"      // QXmlWriter support
55
#include "QSettings_impl.h" // QSettings* support
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
56

57 58 59 60
#ifdef PROTOBUF
#include "persons.pb.h"
#endif

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
61 62 63 64 65
#include <QtCore/qvariant.h>

#include <QtGui/qstandarditemmodel.h>
#include <QtWidgets/qtreeview.h>
#include <QtWidgets/qtableview.h>
66
#include <QtWidgets/qdialog.h>
67 68 69
#include <QtWidgets/qgridlayout.h>
#include <QtWidgets/qtabwidget.h>
#include <QtWidgets/qlabel.h>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
70 71
#include <QtWidgets/qapplication.h>

72 73
#include <tuple>

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
74
// //////////////////////////////////////////////////////////////////////////
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
75
// QValue-enabled types examples
76 77 78

// Using Qt's meta-object system

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
79
#include "data.h"
80 81

// Using an internal bind method
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
82 83 84

struct Person
{
85
    int id; QString firstName, lastName; double height; int age; QVector<Phone> phones; QString comments; QList<Person> children;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
86

87
    QValueEnd zap(QValue&& value) { // works with value->mode()==Read as well as Write
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
88 89 90 91 92 93 94 95 96 97 98
        return value
            .record("Person")
                .sequence("names")
                    .bind(firstName)
                    .bind( lastName)
                    .out()
                .bind("height"  ,height  )
                .bind("age"     ,age  ,-1) // reads null() as -1
                .bind("phones"  ,phones  ) // recursive calls to QTransmogrifier will take care of that part
                .bind("comments",comments)
                .bind("children",children)
99
                .out(); // not calling this would still automagically close opened record() but always trigger a qBindUnexpectedEnd to help write explicitly correct code
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
100 101 102
    }
};

103 104 105
// For comparison purposes:

#include<QtCore/qdebug.h>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
106
QDebug &operator<<(QDebug &out, const Person &p)
107 108 109 110 111 112 113 114 115
{
    return out.nospace() << "Person(" // items count and names are implicit
            << p.firstName << ", " << p.lastName << ", " // sequence is implicit
            << p.height    << ", "
            << p.age       << ", "
            << p.phones    << ", "
            << p.comments  << ", "
            << p.children  << ")";
}
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
116 117 118
#include<QtCore/qdatastream.h>
QDataStream &operator<<(QDataStream &out, const Person &p)
{
119
    return out // record, items count and names are implicit
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
120 121 122 123
            << p.firstName << p.lastName // sequence is implicit
            << p.height
            << p.age
            << p.phones
124
            << p.comments
125 126
            << p.children
            ; // no record delimiter
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
127
}
128
//! Same as operator<<(QDataStream&, Person&) with no possibility to detect errors other than premature end of QDataStream
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
129 130
QDataStream &operator>>(QDataStream &in, Person &p)
{
131
    return in >> p.firstName >> p.lastName >> p.height >> p.age >> p.phones >> p.comments >> p.children;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
132
}
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
133

134
// Using a view type to customize an existing bind using metadata
135 136 137 138

struct PersonsView
{
    QList<Person>& persons;
139

140
    QValueEnd zap(QValue&& value) {
141
        return value
142 143 144
            .meta(qmChildren,"children")
            .meta(qmColumns ,"names,height,age,phones,comments") // to optimize multi-dimensional data formats
            .bind(persons);
145 146 147
    }
};

148 149 150 151 152 153
// View types can even bind nested structures into flattened tables using a foreign key to express parent 1-n children relationships

struct PersonsTable {
    QList<Person>& persons;
    PersonsTable(QList<Person>& ps) : persons(ps) {}

154
    QValueEnd zap(QValue&& v) {
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
        if (v->mode()==Read) {
            ids.clear();
            orphans.clear();
            persons.clear();
            Person p;
            auto s(v.sequence());
            while ((s = bind(p, std::move(s)))) {
                // merge orphans with persons at the end only
            }
            for (int parentId: orphans.keys()) {
                if (parentId && ids.contains(parentId)) {
                    ids[parentId]->children = orphans[parentId];
                }
                else {
                    persons.append(orphans[parentId]);
                }
            }
            return s;
        }
        else if (v->mode()==Write) {
            parentIds.clear();
            ids.clear();
            return flatten(persons, v.sequence());
        }
179
        else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
180 181 182 183 184 185
    }
private:
    QStack<int> parentIds;
    QMap<int,Person*> ids;
    QMap<int,QList<Person>> orphans;

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
186
    QSequence bind(Person& p, QSequence&& s) {
187 188 189 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 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
        if (s->mode()==Read) {
            int parentId=0;
            double height_ft=0.;
            s = s.record()
                    .bind("id"        , p.id       )
                    .bind("parent_id" , parentId   )
                    .bind("first_name", p.firstName)
                    .bind("last_name" , p.lastName )
                    .bind("height_ft" , height_ft  )
                    .bind("age"       , p.age      )
                    .bind("comments"  , p.comments ) // TODO Remove while keeping p.comments untouched
                    .bind("phones"    , p.phones   )
                    .out();
            if (s) {
                p.height = height_ft/3.28;
                orphans[parentId].append(p);
                if (p.id) {
                    Q_ASSERT(!ids.contains(p.id)); // children may be attributed to any of the duplicates
                    ids.insert(p.id, &orphans[parentId].last());
                }
            }
            return std::move(s);
        }
        else if (s->mode()==Write) {
            if (!p.id) {
                int newId=(2019-p.age)*10;
                while (ids.contains(newId)) {
                    newId++;
                    Q_ASSERT(newId<(2020-p.age)*10);
                }
                p.id = newId;
                ids.insert(p.id,&p);
            }

            s = s.record()
                    .bind("id"        , p.id)
                    .bind("parent_id" , parentIds.isEmpty() ? 0 : parentIds.back())
                    .bind("first_name", p.firstName  )
                    .bind("last_name" , p.lastName )
                    .bind("height_ft" , p.height*3.28)
                    .bind("age"       , p.age      )
                    .bind("comments"  , p.comments )
                    .bind("phones"    , p.phones   )
                    .out();

            parentIds.push_back(p.id);
            s = flatten(p.children, std::move(s));
            parentIds.pop_back();

            return std::move(s);
        }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
238
        else { Q_ASSERT_X(!s, Q_FUNC_INFO, "Unsupported v->mode()"); return std::move(s); }
239 240
    }

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
241
    QSequence flatten(QList<Person>& ps, QSequence&& s) {
242 243 244 245 246 247 248
        for (auto&& p : ps) {
            s = bind(p, std::move(s));
        }
        return std::move(s);
    }
};

249
// //////////////////////////////////////////////////////////////////////////
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
250
// QTransmogrifier<T> examples using external bind methods
251 252 253

#include <QtGui/qcolor.h>

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
254
//! QTransmogrifier<QColor> with special support for QDataStream compatibility using meta()
255
template<>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
256
struct QTransmogrifier<QColor> {
257
    static QValueEnd zap(QValue&& v, QColor&& c) {
258
        QColor copy(c);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
259
        return zap(std::move(v),copy);
260
    }
261
    static QValueEnd zap(QValue&& v, QColor& c) {
262 263 264
        if (!c.isValid()) {
            return v.null();
        }
265 266 267 268 269
        QIdentifierLiteral n;
        QAsciiData m;
        do {
            v=v.meta(n, m);
            if (n==qmDataStreamVersion) {
270
                if (m.utf8().toInt()<7) { v->handleError("UnsupportedQDataStreamVersion");
271 272
                    return v.null();
                }
273 274
                QRgba64 c64 = c.rgba64();
                return v.sequence().bind(quint8(c.spec())).bind(quint16(c64.alpha())).bind(quint16(c64.red())).bind(quint16(c64.green())).bind(quint16(c64.blue())).bind(quint16(0)); // TODO pad
275
            }
276
        } while (!n.isNull());
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
277
        QRecord r = v.record();
278
        switch(c.spec()) {
279 280 281
        case QColor::Spec::Rgb : r = r.sequence("RGB" ).bind(quint8(c.red   ())).bind(quint8(c.green        ())).bind(quint8(c.blue     ()))                        .out(); break;
        case QColor::Spec::Hsl : r = r.sequence("HSL" ).bind(qint16(c.hslHue())).bind(quint8(c.hslSaturation())).bind(quint8(c.lightness()))                        .out(); break;
        case QColor::Spec::Hsv : r = r.sequence("HSV" ).bind(qint16(c.hsvHue())).bind(quint8(c.hsvSaturation())).bind(quint8(c.value    ()))                        .out(); break;
282
        case QColor::Spec::Cmyk: r = r.sequence("CMYK").bind(quint8(c.cyan  ())).bind(quint8(c.magenta      ())).bind(quint8(c.yellow   ())).bind(quint8(c.black())).out(); break;
283 284
        default: Q_ASSERT(false);
        }
285 286
        if (c.alpha()<255) { r = r.bind("alpha",quint8(c.alpha())); }
        return r.bind("base",quint8(255)); // Explicits ARGBCMYKSLV components' base (H base is 360 corresponding to color wheel degrees, with -1 used for gray in addition to S being 0)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
287
                                           // QDebug use of float values removes need for base but may lose precision
288 289 290
    }
};

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
291
// //////////////////////////////////////////////////////////////////////////
292
// QAbstractValue basic implementation example
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
293 294

#include <type_traits>
295
#include <QtCore/qbytearray.h>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
296

297
class TextWriter : public QAbstractValueWriter
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
298 299
{
    Q_DISABLE_COPY(TextWriter)
300
    Q_ENABLE_MOVE_DEFAULT(TextWriter)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
301
public:
302
    TextWriter(QByteArray* utf8) : utf8(utf8) { Q_ASSERT(utf8); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
303 304

    // Shortcuts
305 306
    template<typename T> QSequence operator<<(T&& t) { return QCur(this).value().sequence().item().bind(std::forward<T>(t)); }
    template<typename T> QValueEnd bind      (T&& t) { return QCur(this).value()                  .bind(std::forward<T>(t)); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
307
protected:
308 309
    bool trySequence(quint32* =nullptr) { if (0<level++) utf8->append("["); return true; }
    bool tryRecord  (quint32* =nullptr) { if (0<level++) utf8->append("["); return true; }
310
    bool tryAny     (                 ) {                utf8->append("*"); return true; }
311 312 313 314
    bool tryNull    (                 ) {                                   return true; }
    bool tryBind    (  QUtf8DataView u) { utf8->append(u.data(), u.size()); return true; }
    bool tryBind    (        float&& n) { static QByteArray s; s.setNum(double(n),'g',6); return tryBind(QUtf8DataView(s.constData(), s.size())); } // with QDebug precision
    bool tryBind    (       double&& n) { static QByteArray s; s.setNum(       n ,'g',6); return tryBind(QUtf8DataView(s.constData(), s.size())); } // with QDebug precision
315

316 317 318
    bool tryItem(QIdentifier& n) { utf8->append(" ").append(n.utf8()).append(":"); return true; }
    bool tryItem(              ) { utf8->append(" ")                             ; return true; }
    bool tryOut (              ) { if (0<--level) utf8->append("]")              ; return true; }
319

320
    void _meta(QIdentifierLiteral& n, QAsciiData& m) { if (n==qmName) utf8->append('(').append(m.utf8()).append(")"); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
321
private:
322
    template<class T> friend class QModelWriter;
323
    QByteArray* utf8;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
324
    int level = 0;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
325 326
};

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
327
class Text
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
328
{
329
    QByteArray result;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
330
public:
331
    template<typename T> Text(T&& t) { TextWriter(&result).bind(std::forward<T>(t)); }
332 333
    const QByteArray& utf8() const { return result; }
    operator    QByteArray() const { return result; }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
334 335 336 337 338
};

// //////////////////////////////////////////////////////////////////////////
// Tests and Benchmarks

339 340 341 342 343 344 345
// Base dataset
static const char* ascii = "ascii characters are common in QDebug";
static QColor color(45,0,186);
static QVector<double> transform{1./3, 2./3, 1./3, 1.,
                                 2./3, 1./3, 2./3, 1.,
                                 1./3, 2./3, 1./3, 1.,
                                 0.  , 0.  , 0.  , 1.};
346
static Person person{0,"John","Doe",1.75,18,{},"unicode is likely U+01 \x01 + U+1F \x1F + U+A4 ¤ U+B0 ° U+D8 Ø U+FF ÿ",QList<Person>()};
347 348 349
static const double PI=3.141592653589793;
static Phone phone {Phone::Home,"+44 1234567"};

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
350 351
#include <QtCore/qelapsedtimer.h>
#include <QtCore/qfile.h>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
352
#include <QtCore/qdir.h>
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
353 354 355 356 357 358
#include <QtCore/qbuffer.h>
#include <QtCore/qdatastream.h>
#include <QtCore/qdebug.h>
#include <QtCore/qjsonvalue.h>
#include <QtCore/qjsondocument.h>
#include <QtCore/qcborstream.h>
359

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
360 361 362 363
#include <QtGui/qcolor.h>

// NB: It is not possible to use QBENCHMARK to evaluate qDebug because it installs a specific handler

364
#define GROUP(group) if (argc<=1 || strcmp(argv[1],group)==0) { \
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
365 366 367 368 369 370
    double previousTotal=0., groupTotal=0.; \
    bool groupWarmup; \
    do { \
    groupWarmup=(previousTotal<0.01 || std::abs(groupTotal-previousTotal)*100/previousTotal > 1.); \
    previousTotal=groupTotal; \
    groupTotal=0.; \
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
371
    if (previousTotal<0.01) { fprintf(results,"============"); fprintf(samples, "%-14s|================================================================================\n", group); } else fprintf(results,"%-12s",group);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
372 373 374 375 376 377 378 379 380

#define GROUP_STOP \
    if (previousTotal<0.01) fprintf(results,"|total(usecs)|variation(%%)\n"); else fprintf(results,"|%12.1f|%5.1f\n", groupTotal, previousTotal>=0.01 ? std::abs(groupTotal-previousTotal)*100/previousTotal : 0); \
    } while (groupWarmup); }

static QElapsedTimer item;
#define START item.start(); for (int i=0; i<123; ++i)
#define STOP(label,result) { \
    auto usecs=double(item.nsecsElapsed())/1000/123; groupTotal+=usecs; \
381
    if (previousTotal<0.01) { fprintf(results,"|%14s", label); fprintf(samples, "%-14s|%s\n", label, qUtf8Printable(result)); } else fprintf(results,"|%14.1f", usecs); }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
382

383 384
void doGuiExample();

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
385 386 387 388 389
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    // Generated files
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
390 391
    FILE* results; fopen_s(&results, QString("../QBind/results-%1.csv").arg(QDir::current().dirName()).toLocal8Bit().constData(), "w");
    FILE* samples; fopen_s(&samples,         "../QBind/samples.txt"                                                             , "w");
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
392 393 394 395 396
    if (!(results && samples))
        return -1;

    // Temporary buffers
    QString s;
397 398
    QByteArray ba; ba.reserve(1000);
    QBuffer b(&ba); b.open(QIODevice::ReadWrite);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
399 400
    QVariant v;
    QDataStream d(&b);
401

402 403
    qRegisterMetaTypeStreamOperators<QUtf8Data>();

404
    GROUP("builtin>")//========================================================
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
405 406
    {
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
407
            s.resize(0);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
408 409 410 411 412 413 414 415
            QDebug(&s)
                << 1.333333333333f
                << PI
                << ascii
                << false
                << color
                ;
        }
416
        STOP("QDebug",s)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
417
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
418
            ba.resize(0);
419
            auto s = TextWriter(&ba)
420 421 422 423 424
                << 1.333333333333f
                << PI
                << ascii
                << false
                << color
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
425
                ;
426
            s.out(); // to prevent the Q_ASSERT triggered by the implicit sequence() started by the first <<
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
427
        }
428
        STOP("Text",b.buffer())
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
429
        START {
430 431
            ba.resize(0);
            QJsonWriter(&ba).sequence()
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
432 433 434 435 436
                .bind(1.333333333333f)
                .bind(PI)
                .bind(ascii)
                .bind(false)
                .bind(color)
437
                .out();
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
438
        }
439
        STOP("Json",ba)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
440 441 442 443 444 445 446 447 448
        START {
            ba.resize(0);
            QXmlStreamWriter w(&ba);
            QXmlWriter(&w).sequence()
                .bind(1.333333333333f)
                .bind(PI)
                .bind(ascii)
                .bind(false)
                .bind(color)
449
                .out();
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
450
        }
451
        STOP("Xml",ba)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
452 453 454 455 456 457 458 459
        START {
            v.clear();
            QVariantBuilder(&v).sequence()
                .bind(1.333333333333f)
                .bind(PI)
                .bind(ascii)
                .bind(false)
                .bind(color)
460
                .out();
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
461
        }
462
        STOP("Variant",Text(v).utf8())
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
463
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
464
            ba.resize(0);
465
            QCborWriter(&ba).value().sequence(5)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
466 467
                .bind(1.333333333333f)
                .bind(PI)
468
                .bind(ascii)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
469 470
                .bind(false)
                .bind(color)
471
                .out();
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
472
        }
473
        STOP("Cbor",ba.toHex())
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
474
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
475
            b.seek(0); b.buffer().resize(0);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
476 477 478 479 480 481 482
            QCborStreamWriter s(&b);
            s.startArray(5);
                s.append(1.333333333333f);
                s.append(PI);
                s.append(ascii);
                s.append(false);
                s.startMap();
483
                    s.append("RGB");s.startArray();
484 485 486 487
                        s.append(quint8(color.red  ()));
                        s.append(quint8(color.green()));
                        s.append(quint8(color.blue ()));
                        s.endArray();
488
                    s.append("base");s.append(quint8(255));
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
489 490 491
                    s.endMap();
                s.endArray();
        }
492
        STOP("QCborStream",b.buffer().toHex())
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
493
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
494
            b.seek(0); b.buffer().resize(0);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
495 496 497 498 499 500 501
            QDataStream d(&b);
            QDataWriter(&d).sequence()
                .bind(1.333333333333f)
                .bind(PI)
                .bind(ascii)
                .bind(false)
                .bind(color)
502
                .out();
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
503
        }
504
        STOP("Data",b.buffer().toHex())
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
505
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
506
            b.seek(0); b.buffer().resize(0);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
507 508 509 510 511 512 513
            QDataStream(&b)
                    << 1.333333333333f
                    << PI
                    << ascii
                    << false
                    << color;
        }
514
        STOP("QDataStream",b.buffer().toHex())
515 516
        float f = 1.333333333333f; bool boolean = false;
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
517 518
            ba.resize(0);
            ba.append(reinterpret_cast<const char*>(&f      ),sizeof(f      )); // endianness must be specified externally
519 520 521
            ba.append(reinterpret_cast<const char*>(&PI     ),sizeof(PI     ));
            ba.append(                               ascii                   );
            ba.append(reinterpret_cast<const char*>(&boolean),sizeof(boolean));
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
522
            ba.append(reinterpret_cast<const char*>(&color  ),sizeof(color  )); // schema evolution must be specified externally
523
        }
524
        STOP("QByteArray",ba.toHex())
525

526
        QBindable bindables[5];
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
527
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
528 529 530 531 532
            bindables[0] = 1.333333333333f;
            bindables[1] = PI;
            bindables[2] = ascii;
            bindables[3] = false;
            bindables[4] = color;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
533
        }
534
        STOP("Bindables","")
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
535
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
536
            ba.resize(0);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
537
            QCborWriter(&ba).bind(bindables);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
538
        }
539
        STOP("Bindables>Cbor",ba.toHex())
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
540
        START {
541 542
            ba.resize(0);
            QJsonWriter(&ba).bind(bindables);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
543
        }
544
        STOP("Bindables>Json",ba)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
545
    }
546
    GROUP_STOP
547
    GROUP("doubles>")//========================================================
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
548 549
    {
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
550
            s.resize(0);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
551 552 553
            QDebug d(&s);
            for (int i=0; i < 16; i++) {
            d << transform[i];
554
            }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
555
        }
556
        STOP("QDebug",s)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
557
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
558
            ba.resize(0);
559
            TextWriter(&ba).bind(transform);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
560
        }
561
        STOP("Text",b.buffer())
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
562
        START {
563 564
            ba.resize(0);
            QJsonWriter(&ba).bind(transform);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
565
        }
566
        STOP("Json",ba)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
567 568 569 570 571 572
        START {
            ba.resize(0);
            QXmlStreamWriter w(&ba);
            QXmlWriter d(&w);
            d.bind(transform);
        }
573
        STOP("Xml",ba)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
574 575 576 577
        START {
            v.clear();
            QVariantBuilder(&v).bind(transform);
        }
578
        STOP("Variant",Text(v).utf8())
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
579
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
580
            b.seek(0); b.buffer().resize(0);
581
            QCborWriter(&ba).bind(transform);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
582
        }
583
        STOP("Cbor",ba.toHex())
584

EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
585
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
586
            b.seek(0); b.buffer().resize(0);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
587
            QCborStreamWriter s(&b);
588
            constexpr quint64 size = 16;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
589 590
            s.startArray(size);
                for (quint8 i=0; i < size; i++) {
591
                    s.append(transform[i]);
592
                }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
593 594
                s.endArray();
        }
595
        STOP("QCborStream",b.buffer().toHex())
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
596
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
597
            b.seek(0); b.buffer().resize(0);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
598 599 600
            QDataStream d(&b);
            QDataWriter(&d).value().bind(transform);
        }
601
        STOP("Data",b.buffer().toHex())
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
602
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
603
            b.seek(0); b.buffer().resize(0);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
604 605
            QDataStream data(&b);
            data << transform.size();
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
606
            for (int i=0; i < transform.size(); i++) {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
607
                data << transform[i];
608
            }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
609
        }
610
        STOP("QDataStream",b.buffer().toHex())
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
611
        QByteArray str; str.reserve(500);
612
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
613 614
            str.resize(0);
            int size=transform.size();
615 616 617
            str.append(reinterpret_cast<char*>(&size),sizeof(size));
            for (int i=0; i < size; i++) {
                str.append(reinterpret_cast<char*>(&transform[i]),sizeof(transform[i]));
618
            }
619
        }
620
        STOP("QByteArray",str.toHex())
621

622
        QBindable bindable;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
623
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
624
            bindable = transform;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
625
        }
626
        STOP("Bindable","")
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
627
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
628
            ba.resize(0);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
629
            QCborWriter(&ba).bind(bindable);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
630
        }
631
        STOP("Bindable>Cbor",ba.toHex())
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
632
        START {
633 634
            ba.resize(0);
            QJsonWriter(&ba).bind(bindable);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
635
        }
636
        STOP("Bindable>Json",ba)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
637 638
    }
    GROUP_STOP
639
    GROUP("Person>")//=========================================================
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
640 641
    {
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
642
            s.resize(0);
643 644
            QDebug dbg(&s);
            dbg << person;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
645
        }
646
        STOP("QDebug",s)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
647
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
648
            ba.resize(0);
649
            TextWriter(&ba).bind(person);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
650
        }
651
        STOP("Text",b.buffer())
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
652
        START {
653 654
            ba.resize(0);
            QJsonWriter(&ba).bind(person);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
655
        }
656
        STOP("Json",ba)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
657 658 659 660 661 662
        START {
            ba.resize(0);
            QXmlStreamWriter w(&ba);
            QXmlWriter d(&w);
            d.bind(person);
        }
663
        STOP("Xml",ba)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
664 665 666 667
        START {
            v.clear();
            QVariantBuilder(&v).bind(person);
        }
668
        STOP("Variant",Text(v).utf8())
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
669
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
670
            ba.resize(0);
671
            QCborWriter(&ba).bind(person);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
672
        }
673
        STOP("Cbor",ba.toHex())
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
674
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
675
            b.seek(0); b.buffer().resize(0);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
676 677 678 679 680 681
            QCborStreamWriter s(&b);
            s.startMap();
                s.append("names");s.startArray();
                    s.append(person.firstName);
                    s.append(person. lastName);
                    s.endArray();
682
                s.append("height");s.append(person.height  );
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
683
                s.append("age"   );s.append(person.age     );
684 685
                int size = person.phones.size();
                s.append("phones");s.startArray(quint64(size));
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
686
                    for (int i=0; i < size; i++) {
687
                        s.startMap();
688
                            s.append("type"  );s.append(person.phones[i]._t);
689 690
                            s.append("number");s.append(person.phones[i]._n);
                            s.endMap();
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
691
                    }
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
692
                    s.endArray();
693 694 695
                s.append("comments");s.append(person.comments);
                s.append("children");s.startArray(quint64(person.children.size()));
                    // Would need recursion
696
                    s.endArray();
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
697 698
            s.endMap();
        }
699
        STOP("QCborStream",b.buffer().toHex())
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
700
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
701
            b.seek(0); b.buffer().resize(0);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
702 703 704
            QDataStream d(&b);
            QDataWriter(&d).value().bind(person);
        }
705
        STOP("Data",b.buffer().toHex())
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
706
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
707
            b.seek(0); b.buffer().resize(0);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
708 709 710
            QDataStream d(&b);
            d << person;
        }
711
        STOP("QDataStream",b.buffer().toHex())
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
712
        QByteArray str; str.reserve(100); int s;
713
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
714
            str.resize(0);
715 716 717
            str.append(reinterpret_cast<const char*>(person.firstName.constData()),person.firstName.size());// encoding must be specified externally
            str.append(reinterpret_cast<const char*>(person.lastName .constData()),person.lastName .size());
            str.append(reinterpret_cast<const char*>(&person.height),sizeof(person.height));                // endianness must be specified externally
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
718
            str.append(reinterpret_cast<const char*>(&person.age   ),sizeof(person.age   ));
719 720 721 722 723 724 725 726 727 728 729
            s=person.phones.size();
            str.append(reinterpret_cast<const char*>(&s),sizeof(s));
            for (int i=0; i<s; ++i) {
                str.append(reinterpret_cast<const char*>(&person.phones[i]._t),sizeof(person.phones[i]._t));str.append(person.phones[i]._n);
            }
            str.append(reinterpret_cast<const char*>(person.comments.constData()),person.comments.size());                                                           // byte order must be specified externally
            s=person.children.size();
            str.append(reinterpret_cast<const char*>(&s),sizeof(s));
            for (int i=0; i<s; ++i) {
                Q_ASSERT(false); // Would need recursion
            }
730
        }
731
        STOP("QByteArray",str.toHex())
732
#ifdef PROTOBUF
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
733
        std::string buf;
734 735 736 737 738 739 740
        pb::Person pb;
        pb.set_firstname("John");
        pb.set_lastname("Doe");
        pb.set_height(1.75);
        pb.set_age(18);
        pb.set_comments("unicode is likely U+01 \x01 + U+1F \x1F + U+A4 ¤ U+B0 ° U+D8 Ø U+FF ÿ");
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
741
            buf.resize(0);
742 743 744 745
            buf = pb.SerializeAsString();
        }
        STOP("protobuf",QByteArray::fromStdString(buf).toHex());
#endif
746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763
        START {
            ba.resize(0);
            QJsonObject p{
                {"names"   , QJsonArray{person.firstName, person.lastName} },
                {"height"  , person.height},
                {"age"     , person.age},
                {"comments", person.comments},
                {"children", QJsonArray()}}; // would need recursion
            QJsonArray phones;
            for (auto&& phone : person.phones) {
                phones.append(QJsonObject{
                    {"type"  ,quint8(phone._t)},
                    {"number",       phone._n }});
            }
            p["phones"] = phones;
            ba = QJsonDocument(p).toJson(QJsonDocument::Compact);
        }
        STOP("QJsonDocument",ba)
764

765
        QBindable bindable;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
766
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
767
            bindable = person;
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
768
        }
769
        STOP("Bindable","")
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
770
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
771
            ba.resize(0);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
772
            QCborWriter(&ba).bind(bindable);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
773
        }
774
        STOP("Bindable>Cbor",ba.toHex())
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
775
        START {
776 777
            ba.resize(0);
            QJsonWriter(&ba).bind(bindable);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
778
        }
779
        STOP("Bindable>Json",ba)
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
780 781
    }
    GROUP_STOP
782 783 784 785 786 787 788
    GROUP("Phone>")//=========================================================
    {
        START {
            s.resize(0);
            QDebug dbg(&s);
            dbg << phone;
        }
789
        STOP("QDebug",s)
790 791 792 793
        START {
            ba.resize(0);
            TextWriter(&ba).bind(phone);
        }
794
        STOP("Text",b.buffer())
795
        START {
796 797
            ba.resize(0);
            QJsonWriter(&ba).bind(phone);
798
        }
799
        STOP("Json",ba)
800 801 802 803 804 805
        START {
            ba.resize(0);
            QXmlStreamWriter w(&ba);
            QXmlWriter d(&w);
            d.bind(phone);
        }
806
        STOP("Xml",ba)
807 808 809 810
        START {
            v.clear();
            QVariantBuilder(&v).bind(phone);
        }
811
        STOP("Variant",Text(v).utf8())
812 813 814 815
        START {
            ba.resize(0);
            QCborWriter(&ba).bind(phone);
        }
816
        STOP("Cbor",ba.toHex())
817 818 819 820 821 822 823 824
        START {
            b.seek(0); b.buffer().resize(0);
            QCborStreamWriter s(&b);
            s.startMap();
                s.append("type"  );s.append(phone._t);
                s.append("number");s.append(phone._n);
                s.endMap();
        }
825
        STOP("QCborStream",b.buffer().toHex())
826 827 828 829 830
        START {
            b.seek(0); b.buffer().resize(0);
            QDataStream d(&b);
            QDataWriter(&d).value().bind(phone);
        }
831
        STOP("Data",b.buffer().toHex())
832 833 834 835 836
        START {
            b.seek(0); b.buffer().resize(0);
            QDataStream d(&b);
            d << phone;
        }
837
        STOP("QDataStream",b.buffer().toHex())
838
        QByteArray str; str.reserve(100);
839 840 841 842
        START {
            str.resize(0);
            str.append(reinterpret_cast<const char*>(&phone._t),sizeof(phone._t));str.append(phone._n);
        }
843
        STOP("QByteArray",str.toHex())
844

845
        QBindable bindable;
846
        START {
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
847
            bindable = phone;
848
        }
849
        STOP("Bindable","")
850 851
        START {
            ba.resize(0);
EXT Arnaud Clère's avatar
EXT Arnaud Clère committed
852
            QCborWriter(&ba).bind(bindable);
853
        }
854
        STOP("Bindable>Cbor",ba.toHex())
855
        START {
856 857
            ba.resize(0);
            QJsonWriter(&ba).bind(bindable);
858
        }
859
        STOP("Bindable>Json",ba)
860 861 862
    }
    GROUP_STOP

863 864 865
    QStringList errors;
    auto handler = [&errors](QIdentifierLiteral error, QString context) {
        errors.append(context.append(error.latin1()));
866
        return true;
867
    };