Pregunta

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.

¿Fue útil?

Solución 3

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.

Otros consejos

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.

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top