Commit bc4cdfc3 authored by EXT Arnaud Clère's avatar EXT Arnaud Clère

Added rough QSettings support

parent b5a80eb4
......@@ -52,6 +52,7 @@ HEADERS += \
QBind_impl.h \
QCbor_impl.h \
QJson_impl.h \
QSettings_impl.h \
QVariant_impl.h \
QModel_impl.h \
QData_impl.h \
......@@ -62,4 +63,5 @@ DISTFILES += \
README.md \
design.md \
persons.proto \
sample.ini \
samples.txt
/****************************************************************************
* **
* ** 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/qsettings.h>
#include <QtCore/qvariant.h>
#include <QtCore/qstack.h>
#include "QBind_impl.h"
// //////////////////////////////////////////////////////////////////////////
// QBind<T,QVariant*> support for the fixed set of QSettingsWriter's BindNative types
class QSettingsWriter : public IWriter
{
Q_DISABLE_COPY(QSettingsWriter)
public:
QSettingsWriter(QSettings* s) : settings(s) { Q_ASSERT(s); levels.push(Level("")); }
// Shortcuts
/**/ Val<Cursor> value ( ) { return Cursor(this).value(); }
/**/ Seq<Cursor> sequence(quint32* s=nullptr) { return Cursor(this).value().sequence ( s); }
template<typename T> Cursor bind ( T&& t) { return Cursor(this).value().bind(std::forward<T>(t)); }
protected:
template<typename T>
bool _bind ( T&& t) { settings->setValue(key(), QVariant::fromValue(t) ); return true; }
bool _bind ( quint8&& t) { settings->setValue(key(), QVariant (int(t))); return true; }
bool _bind ( quint16&& t) { settings->setValue(key(), QVariant (int(t))); return true; }
bool _bind ( quint32&& t) { settings->setValue(key(), QVariant (int(t))); return true; }
bool _bind (const char* u) { settings->setValue(key(), QVariant (u) ); return true; }
bool _null ( ) { settings->setValue(key(), QVariant ( ) ); return true; }
bool _sequence(quint32* =nullptr) { settings->beginGroup(key()); levels.push(Level( )); return true; }
bool _record (quint32* =nullptr) { settings->beginGroup(key()); levels.push(Level("")); return true; }
bool _item(QName n) { levels.last().key=n; return true; }
bool _item( ) { levels.last().idx++; return true; }
bool _out ( ) { levels.pop (); settings->endGroup(); return true; }
private:
QString key() {
Q_ASSERT(!levels.isEmpty());
return levels.last().key!=nullptr
? QString (levels.last().key)
: QString::number(levels.last().idx);
}
QSettings* settings;
struct Level { QName key; quint32 idx=0; Level(QName k=nullptr) : key(k) {} };
QStack<Level> levels = QStack<Level>(); //!< minimal dynamic context to implement out() and ensure actual building in case QSettingsWriter is abandoned
};
// --------------------------------------------------------------------------
class QSettingsReader : public IReader
{
Q_DISABLE_COPY(QSettingsReader)
public:
Q_ENABLE_MOVE(QSettingsReader, std::swap(isChoice, o.isChoice); )
QSettingsReader(QSettings* s) : settings(s) { Q_ASSERT(s); levels.push(Level("")); }
struct Error { const char* error; QUtf8String path; template<class T> T bind(Val<T>&& value) { return value.bind(QUtf8String(error)+' '+path); } };
QVector<Error> errors;
QUtf8String currentPath() {
QUtf8String path;
Q_FOREACH(Level l, levels) {
if (l.key) { path.append('/').append( l.key ); }
else { path.append('/').append(QUtf8String::number(l.idx)); }
}
return path;
}
// Shortcuts
template<typename T> Cursor bind(T&& t) { return Cursor(this).value().bind(std::forward<T>(t)); }
protected:
template<typename T>
bool _bind(T& t) { return set(t, "Expected declared metatype T"); }
bool _bind(QUtf8String& t) { return set(t, qBindExpectedText ); }
bool _bind( QString& t) { return set(t, qBindExpectedText ); }
bool _bind( bool& t) { return set(t, qBindExpectedBoolean); }
bool _bind( QByteArray& t) { return set(t, qBindExpectedBytes ); }
// Convert numerical types to strictly larger ones // TODO convert all compatible values
bool _bind( qint8& t) { return set(t, qBindExpectedInteger ); }
bool _bind( qint16& t) { return set(t, qBindExpectedInteger ); }
bool _bind( qint32& t) { return set(t, qBindExpectedInteger ); }
bool _bind( qint64& t) { return set(t, qBindExpectedInteger ); }
bool _bind( quint8& t) { return set(t, qBindExpectedPositiveNumber); }
bool _bind( quint16& t) { return set(t, qBindExpectedPositiveNumber); }
bool _bind( quint32& t) { return set(t, qBindExpectedPositiveNumber); }
bool _bind( quint64& t) { return set(t, qBindExpectedPositiveNumber); }
bool _bind( float& t) { return set(t, qBindExpectedDecimal ); }
bool _bind( double& t) { return set(t, qBindExpectedDecimal ); }
bool _sequence(quint32* =nullptr) { if (levels.last().isGroup) { levels.push(Level( )); return true; } _reportError(qBindExpectedSequence); return false; }
bool _record (quint32* =nullptr) { if (levels.last().isGroup) { levels.push(Level("")); return true; } _reportError(qBindExpectedRecord ); return false; }
bool _null ( ) { if (settings->value(key()).isNull()) { return true; } _reportError(qBindExpectedNull ); return false; }
bool _item(QUtf8String& k) { levels.last().key=k; return true; }
bool _item( ) { levels.last().idx++; return true; }
bool _out ( ) { levels.pop(); settings->endGroup(); return true; }
bool _isOk() { return settings; }
void _setChoice(bool v) { isChoice=v; }
void _reportError(const char* error) { if (!isChoice) errors.append(Error{ error, currentPath() }); }
private:
template<typename T>
bool set(T& t, const char* error) { QVariant v = settings->value(key()); if (v.convert(qMetaTypeId<T>())) { t = v.value<T>(); return true; } _reportError(error); return false; }
QString key() {
Q_ASSERT(!levels.isEmpty());
return levels.last().key!=nullptr
? QString (levels.last().key)
: QString::number(levels.last().idx);
}
QSettings* settings;
struct Level { QName key; int idx=-1; bool isGroup=true; Level(QName n=nullptr, bool isGroup=true) : key(n), isGroup(isGroup) {} };
QStack<Level> levels;
bool isChoice = false;
};
......@@ -3,7 +3,7 @@
QBind was developed to demonstrate the feasibility and advantages of a novel (de)serialization mechanism on top of
existing [Qt](http://qt.io) data formats (Settings, QDataStream, Json, Cbor, Xml) and generic data types (containers, QVariant, QMetaObject, etc.).
> **DISLAIMER** It is not currently unit-tested but only provided with a [sample and benchmark](main.cpp). Also, the
> **DISLAIMER** It is not currently unit-tested but provided with a [sample and benchmark](main.cpp). Also, the
> implementation is written in a concise style that supported the multiple refactorings but does not help explaining it.
> The README and [design](design.md) documentation should be read before trying to understand the implementation.
......@@ -45,7 +45,7 @@ See below:
* **W4. Format can be changed at runtime**
(a compiled tracepoint in a library must be able to generate Json or Cbor as desired by library user)
* **W5. Good support of Qt data**:
1. almost all features of simple data (QJson..., QDataStream, *QSettings (TBD)*)
1. almost all features of simple data (QJson..., QDataStream, QSettings)
2. most features of complex data (QCbor..., QXml..., QMetaObject, QModel...)
* **W6. No restriction on output data size**
(some restrictions may apply with specific implementations that may, e.g. store context for each data structure levels)
......@@ -75,7 +75,7 @@ for data formats supporting it like [CBOR value sharing tags](http://cbor.schmor
## The key idea
QBind is more general than (de)serialization and should be understood as a generic way to traverse[^1] a C++ dataset along with
QBind is more general than (de)serialization and should be understood as a generic way to traverse[^1] a C++ dataset and
another generic dataset, binding the related parts together. In effect:
* the traversal may be partial, leaving out unrelated dataset parts (satisfying R2)
* the same traversal may be used to:
......
......@@ -52,6 +52,7 @@
#include "QVariant_impl.h" // QVariantBuilder, QVariantVisitor and QBind<QVariant,_> support
#include "QModel_impl.h" // Q*ModelWriter support
#include "QXml_impl.h" // QXmlWriter support
#include "QSettings_impl.h" // QSettings* support
#ifdef PROTOBUF
#include "persons.pb.h"
......@@ -944,6 +945,31 @@ int main(int argc, char *argv[])
STOP("Cbor>Json",QString(b.buffer()+" | "+Text(readerErrors)))
}
GROUP_STOP
GROUP("Person<>Settings")//====================================================
{
QFileInfo iniFile(QDir::currentPath()+"/../QBind");
QSettings::setPath(QSettings::IniFormat,QSettings::UserScope,iniFile.path());
QSettings ini(QSettings::IniFormat,QSettings::UserScope,"QBind","sample");
QSettings nat( QSettings::UserScope,"QBind","sample");
QVector<QSettingsReader::Error> errors;
//---------------------------------------------------------------------
START {
QSettingsWriter(&nat).bind(person);
}
STOP("P>Settings",QString(Text(person)))
START {
QSettingsReader r(&nat);
r.bind(person);
errors = r.errors;
}
STOP("Settings>P",QString(Text(person)+" | "+Text(errors)))
START {
QSettingsWriter(&ini).bind(person);
}
STOP("P>Settings",iniFile.fileName())
//---------------------------------------------------------------------
}
GROUP_STOP
if (argc<=1 || strcmp(argv[1],"gui")==0) {
doGuiExample();
......
[names]
1=John
2=Doe
[General]
height=1.75
age=18
comments=unicode is likely U+01 \x1 + U+1F \x1f + U+A4 \xa4 U+B0 \xb0 U+D8 \xd8 U+FF \xff
......@@ -73,3 +73,7 @@ CborValue>P |(Person)[ names:[ John Doe] height:1.75 age:-1 phones:[] comments
Cbor>CborValue|{"names": ["John", "Doe"], "height": 1.75, "age": -1, "phones": [], "comments": "", "children": []} | []
CborValue>Cbor|a6656e616d657382644a6f686e63446f6566686569676874fb3ffc00000000000063616765206670686f6e65738068636f6d6d656e747360686368696c6472656e80
Cbor>Json |{"names":["John","Doe"],"height":1.75,"age":} | []
Person<>Settings |================================================================================
P>Settings |(Person)[ names:[ John Doe] height:1.75 age:18 phones:[] comments:unicode is likely U+01  + U+1F  + U+A4 ¤ U+B0 ° U+D8 Ø U+FF ÿ children:[]]
Settings>P |(Person)[ names:[ John Doe] height:1.75 age:18 phones:[] comments:unicode is likely U+01  + U+1F  + U+A4 ¤ U+B0 ° U+D8 Ø U+FF ÿ children:[]] | []
P>Settings |QBind
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment