Funktionszeiger in QSettings::registerFormat(...)

Alles rund um die Programmierung mit Qt
Antworten
bobcat
Beiträge: 125
Registriert: 21. April 2010 14:51

Funktionszeiger in QSettings::registerFormat(...)

Beitrag von bobcat » 12. Februar 2018 15:03

Ich möchte ein eigenes Format für QSettings registrieren. Hintergrund siehe hier:
viewtopic.php?f=1&t=18562

Ich habe dazu zwei eigene ReadFunc und WriteFunc geschrieben, die ich jetzt registrieren möchte. Allerdings gelingt es mir nicht, einen Funktionszeiger auf meine Funktionen korrekt zu implementieren.
Ich arbeite mit einem Pimpl (QtSettings::Data), um die Implementierungsdetails meiner Klasse in der .cpp-Datei zu verstecken. Damit bekomme ich einen Compilerfehler:

Code: Alles auswählen

'&': Ungültige Operation auf Ausdruck einer gebundenen Memberfunktion
Der Fehler tritt in der folgenden Codezeile auf:

Code: Alles auswählen

bool (*readFctn)(QIODevice&, QSettings::SettingsMap&) = &readFromInternalMap;
Hier der relevante Ausschnitt aus meinem gesamten Code. Kann mir jemand helfen, wie ich den Funktionszeiger korrekt implementiere, bzw. was hier schiefgeht?

Code: Alles auswählen

struct QtSettings::Data
{
    QSettings* qSettings;

    // Wird nur verwendet, wenn die Settings nicht an eine Datei angebunden werden sollen.
    QMap<QString, QVariant> internalMap;
    bool readFromInternalMap(QIODevice& device, QSettings::SettingsMap& map);
    bool writeToInternalMap(QIODevice& device, const QSettings::SettingsMap& map);

    // Data zur Anbindung an eine interne QMap
    Data() :
        qSettings(0)
    {
        bool (*readFctn)(QIODevice&, QSettings::SettingsMap&) = &readFromInternalMap;
        bool (*writeFctn)(QIODevice&, const QSettings::SettingsMap&) = &writeToInternalMap;

        const QSettings::Format InternalMapFormat = QSettings::registerFormat("xml", readFctn, writeFctn);
        qSettings = new QSettings(InternalMapFormat, QSettings::UserScope, "myCompany");
    }

    // Data zur Anbindung an ein ini-File
    Data (const QString& filename) :
        qSettings(0)
    {
        qSettings = new QSettings(filename, QSettings::IniFormat);
    }

    ~Data()
    {
        delete qSettings;
    }

};

bool QtSettings::Data::readFromInternalMap(QIODevice& device, QSettings::SettingsMap& map)
{
    Q_UNUSED(device);
    map = internalMap;
    return true;
}

bool QtSettings::Data::writeToInternalMap(QIODevice& device, const QSettings::SettingsMap& map)
{
    Q_UNUSED(device);
    internalMap = map;
    return true;
}


QtSettings::QtSettings() :
    m(0)
{
    m = new Data();
}

QtSettings::QtSettings(const QString& filename) :
    m(0)
{
    m = new Data(filename);
}

QtSettings::~QtSettings()
{
    delete m;
}

bobcat
Beiträge: 125
Registriert: 21. April 2010 14:51

Re: Funktionszeiger in QSettings::registerFormat(...)

Beitrag von bobcat » 12. Februar 2018 16:13

Wenn ich die ReadFunc und WriteFunc global definiere (inkl. der verwendeten internalMap), dann kompiliert der Code.

Allerdings würde ich das ungern machen: Ich habe in meiner Anwendung eine beliebige Anzahl an Geräten, die ich ansteuere, und die alle über ein eigenes QtSettings verfügen. Hier möchte ich prinzipiell vermeiden, dass diese alle ihre Einstellungen in derselben internalMap speichern.
Ich verstehe allerdings, dass es problematisch ist sein könnte, wenn ich QSettings::registerFormat(...) mehrfach aus verschiedenen Instanzen aufrufe.
Was ich möchte: Mehrere QSettings Objekte, die ich alle mittels registerFormat(...) an eine unterschiedliche interne QMap anbinden kann. Meint ihr, das geht?

Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: Funktionszeiger in QSettings::registerFormat(...)

Beitrag von Christian81 » 12. Februar 2018 19:55

Ist ja auch klar - QtSettings::Data::readFromInternalMap ist eine Funktion in einer Klasse - wie sollte der Linker wissen zu welcher Instanz die Funktion gehört wenn Du nur den Pointer zu readFromInternalMap übergibst?

Eine Lösung wäre eine kleine Lambda-Funktion die den this-Pointer übergeben bekommt und dann die richtige Funktion aufrufen kann.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung

bobcat
Beiträge: 125
Registriert: 21. April 2010 14:51

Re: Funktionszeiger in QSettings::registerFormat(...)

Beitrag von bobcat » 12. Februar 2018 21:51

Vielen Dank für die Hinweise! Ich würde das gerne ausprobieren, aber habe keine Erfahrung mit Lambdas und weiß auf Anhieb nicht, wie das aussehen würde. Ist das ein Konstrukt, das es in älterem C++ schon gibt oder erst in C++11? Ich bin hier nämlich leider noch auf einem älteren Compiler (VS2010). Hast Du vielleicht einen Link mit einem Beispiel zu Lambda?
Zuletzt geändert von bobcat am 12. Februar 2018 22:09, insgesamt 1-mal geändert.

Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: Funktionszeiger in QSettings::registerFormat(...)

Beitrag von Christian81 » 12. Februar 2018 21:59

Lambdas sollte laut msdn auch mit msvc 2010 zumindest rudimentär funktionieren: https://msdn.microsoft.com/de-de/librar ... .100).aspx

Achtung, nicht getestet ob es kompiliert:

Code: Alles auswählen

Data()
{
  auto readFn = [this](QIODevice &io, QSettings::SettingsMap &sm) { this->readFromInternalMap(io, sm); }
  auto writeFn = [this](QIODevice &io, const QSettings::SettingsMap &sm) { this->writeToInternalMap(io, sm); }
  const QSettings::Format InternalMapFormat = QSettings::registerFormat("xml", readFn, writeFn);
  qSettings = new QSettings(InternalMapFormat, QSettings::UserScope, "myCompany");
}
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung

bobcat
Beiträge: 125
Registriert: 21. April 2010 14:51

Re: Funktionszeiger in QSettings::registerFormat(...)

Beitrag von bobcat » 12. Februar 2018 22:14

Danke, ich schau mal, ob ich das bei mir kompiliert bekomme.
Noch eine Verständnisfrage: Brauche ich "auto"? Eine Websuche sagt mir, dass das auch zu C++11 gehört.

Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: Funktionszeiger in QSettings::registerFormat(...)

Beitrag von Christian81 » 12. Februar 2018 22:22

Die Funktionstyp-Definitionen bekomme ich so gerade nicht raus - ist zu kompliziert :)
Wenn Du die beiden Lambdas direkt in der registerFormat() - Funktion übergibst geht es auch ohne
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung

bobcat
Beiträge: 125
Registriert: 21. April 2010 14:51

Re: Funktionszeiger in QSettings::registerFormat(...)

Beitrag von bobcat » 12. Februar 2018 22:44

Bei mir wirft der Compiler noch einen Fehler (in beiden Versionen unten):

Code: Alles auswählen

error C2664: 'QSettings::registerFormat': Konvertierung des Parameters 2 von '`anonymous-namespace'::<lambda0>' in 'QSettings::ReadFunc' nicht möglich.
Hier ist mein Code:

Code: Alles auswählen

auto readFn  = [this](QIODevice &io,       QSettings::SettingsMap &sm) {this->readFromInternalMap(io, sm);};
auto writeFn = [this](QIODevice &io, const QSettings::SettingsMap &sm) {this->writeToInternalMap(io, sm); };
const QSettings::Format InternalMapFormat = QSettings::registerFormat("iMap", readFn, writeFn);
oder

Code: Alles auswählen

const QSettings::Format InternalMapFormat = QSettings::registerFormat("iMap",
                                                                      [this](QIODevice &io, QSettings::SettingsMap &sm) {this->readFromInternalMap(io, sm);},
                                                                      [this](QIODevice &io, const QSettings::SettingsMap &sm) {this->writeToInternalMap(io, sm); });

Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: Funktionszeiger in QSettings::registerFormat(...)

Beitrag von Christian81 » 13. Februar 2018 00:04

Sehe den Fehler - die Funktionen geben ein bool zurück, meine Lambda-Funktionen nicht.
Jetzt auch ohne auto:

Code: Alles auswählen

QSettings::ReadFunc  readFn  = [this](QIODevice &io,       QSettings::SettingsMap &sm) -> bool {this->readFromInternalMap(io, sm); return true; };
QSettings::WriteFunc writeFn = [this](QIODevice &io, const QSettings::SettingsMap &sm) -> bool {this->writeToInternalMap(io, sm); return true; };
Das '-> bool' ist mit einem richtigen C++11 - Compiler nicht nötig, er bekommt das selbst heraus. Schadet aber auch nicht.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung

bobcat
Beiträge: 125
Registriert: 21. April 2010 14:51

Re: Funktionszeiger in QSettings::registerFormat(...)

Beitrag von bobcat » 13. Februar 2018 13:05

Eine harte Nuss ... ich habe Deine Änderungen ausprobiert, bekomme aber immer noch den folgenden Compilerfehler (und entsprechend für die WriteFunc):

Code: Alles auswählen

error C2440: 'Initialisierung': '`anonymous-namespace'::<lambda0>' kann nicht in 'QSettings::ReadFunc' konvertiert werden

bobcat
Beiträge: 125
Registriert: 21. April 2010 14:51

Re: Funktionszeiger in QSettings::registerFormat(...)

Beitrag von bobcat » 13. Februar 2018 15:14

Für alle, die dieses Thema hier lesen und auch gerne mehr über Lambdas wissen möchten, hier noch ein guter Blogppost:
https://blog.feabhas.com/2014/03/demyst ... c-lambdas/

Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: Funktionszeiger in QSettings::registerFormat(...)

Beitrag von Christian81 » 13. Februar 2018 19:10

Funktioniert bei mir problemlos. Ist der Compiler wohl zu alt. 8 Jahre ...
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung

bobcat
Beiträge: 125
Registriert: 21. April 2010 14:51

Re: Funktionszeiger in QSettings::registerFormat(...)

Beitrag von bobcat » 13. Februar 2018 20:01

Vermutlich. Wir haben ein Compilerupgrade sowieso im Blick, da überfällig. Vielen Dank nochmal für die hilfreichen Kommentare!

Antworten