Seite 1 von 1

Funktionszeiger in QSettings::registerFormat(...)

Verfasst: 12. Februar 2018 14:03
von bobcat
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;
}

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

Verfasst: 12. Februar 2018 15:13
von bobcat
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?

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

Verfasst: 12. Februar 2018 18:55
von Christian81
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.

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

Verfasst: 12. Februar 2018 20:51
von bobcat
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?

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

Verfasst: 12. Februar 2018 20:59
von Christian81
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");
}

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

Verfasst: 12. Februar 2018 21:14
von bobcat
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.

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

Verfasst: 12. Februar 2018 21:22
von Christian81
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

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

Verfasst: 12. Februar 2018 21:44
von bobcat
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); });

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

Verfasst: 12. Februar 2018 23:04
von Christian81
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.

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

Verfasst: 13. Februar 2018 12:05
von bobcat
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

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

Verfasst: 13. Februar 2018 14:14
von bobcat
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/

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

Verfasst: 13. Februar 2018 18:10
von Christian81
Funktioniert bei mir problemlos. Ist der Compiler wohl zu alt. 8 Jahre ...

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

Verfasst: 13. Februar 2018 19:01
von bobcat
Vermutlich. Wir haben ein Compilerupgrade sowieso im Blick, da überfällig. Vielen Dank nochmal für die hilfreichen Kommentare!