2

I am writing and reading string and int values using a file-backed QSettings object. When I later try to read the values from a different process, the values are read as strings instead of int.

This is the code I am using to write values:

QSettings settings("TestQSettings.ini", QSettings::IniFormat);
settings.setValue("AAA",QString("111"));
settings.setValue("BBB",222);

This is the file created:

[General]
AAA=111
BBB=222

This is the code I am using to read values:

QVariant qvar = settings.value("AAA");
std::cout << "AAA type " << qvar.type() << std::endl;
qvar = settings.value("BBB");
std::cout << "BBB type " << qvar.type() << std::endl;

If I run this code from the same process:

AAA type 10
BBB type 2

If I run this code from a different process:

AAA type 10
BBB type 10

I know it's possible to convert the types after they have been read. Unfortunately, this solution will require modifying Windows legacy cross-platform code which I prefer not to modify, for example multiple calls to RegQueryValueEx().

Is it possible to store and read the type information for strings and integers?

For example, Strings will have quotes "" and integers will not:

[General]
AAA="111"
BBB=222

This problem is present on both Qt 4 and Qt 5, on Linux.

3
  • 1
    I couldn't find an actual question in your post. And I am afraid .ini files don't store type information, so if the question is "how do I make the .ini file keep the type," I am afraid the answer is "you don't." Commented Oct 14, 2013 at 7:37
  • 2
    your program should be knowing of what type it has to expect of its ini file. so if you store a int there, convert the QVariant to an integer and if it fails, throw exception ... simple as that Commented Oct 14, 2013 at 8:49
  • Updated the question. It's not that simple to modify the code, since I'm doing a wrapper for Windows registry calls from another project. Too many calls, and I don't want to modify that code too much. Commented Oct 14, 2013 at 10:49

3 Answers 3

1

Whoa whoa, are you using .ini files or the registry?

With .ini files it's obviously impossible to know what the type was, since it's all a string. You can attempt conversion of the variant to an integer (don't use canConvert!), and assume it's an integer if it converts into one.

With the registry, QSettings will work as you expect it to.

I really don't see what the problem is. Don't use .ini files if you wish to retain type information. You'd face exactly the same problems if you wrote the code by hand in a platform-dependent manner.

You can explicitly write quoted strings into the .ini files, and check for presence of quotes when reading them back. If the quotes are not present, you can try conversion to an integer.

Sign up to request clarification or add additional context in comments.

3 Comments

I'm using ini files on Linux.
I'm using ini files on Linux. But the code I'm compiling with is shared with a Windows project and should compile for both platforms. I am looking into QStringList with the size of 1. This looks promising.
If I add quotes on both sides of the string, the string is stored without the quotes.
1

I solved this problem for a component which needs to save and restore variants of arbitrary type, without knowing what its clients expect. The solution was to store the variant's typeName() alongside each value:

void store(QSettings& settings, const QString& key, const QVariant& value)
{
    settings.setValue(key+"value", value);
    settings.setValue(key+"type", value.typeName());
}

When reading back, we also read the type name, and convert() the variant if it's not already the correct type, before returning it.

QVariant retrieve(const QSettings& settings, const QString& key)
{
    auto value = settings.value(key+"value");
    const auto typeName = settings.value(key+"type").toString();

    const bool wasNull = value.isNull();                         // NOTE 1
    const auto t = QMetaType::type(typeName.toUtf8());           // NOTE 2

    if (value.userType() != t && !value.convert(t) && !wasNull) {
        // restore value that was cleared by the failed convert()
        value = settings.value(key+"value");
        qWarning() << "Failed to convert value" << value << "to" << typeName;
    }

    return value;
}

Notes

  1. The wasNull variable is in there because of this niggle of convert():

    Warning: For historical reasons, converting a null QVariant results in a null value of the desired type (e.g., an empty string for QString) and a result of false.

    In this case, we need to ignore the misleading return value, and keep the successfully-converted null variant of the correct type.


  1. It's not clear that UTF-8 is the correct encoding for QMetaType names (perhaps local 8-bit is assumed?); my types are all ASCII, so I just use toLatin1() instead, which might be faster. If it were an issue, I'd use QString::fromLatin1 in the store() method (instead of implicit char* to QString conversion), to ensure a clean round-trip.

    If the type name is not found, t will be QMetaType::UnknownType; that's okay, because convert() will then fail, and we'll return the unconverted variant (or a null). It's not great, but it's a corner case that won't happen in normal usage, and my system will recover reasonably quickly.

Comments

0

Turns out the solution was very simple.

When values are written to the INI file, the type is known. I am appending to the value "\"STRING right before SetValue

When values are read back from the INI file. I verify that string types have the above postfix. If they do, I chop the postfix off. If they don't I assume they are integers instead of strings.

Works like a charm!

Thanks to you all and especially @Kuba Ober for practically handing out the solution.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.