Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assigning to nested QVariantMap

#include <QtCore/QCoreApplication>
#include <QVariant>
#include <QtDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QVariantMap map;
    map["foo"] = QVariant(QVariantMap());
    map["baz"] = "asdf";
    qvariant_cast<QVariantMap>(map["foo"])["bar"] = "a";

    qDebug() << qvariant_cast<QVariantMap>(map["foo"])["bar"].toString();
    qDebug() << map["baz"].toString();

    return a.exec();
}

I am trying to assign to a QVariant within a nested QVariantMap. The first qDebug() outputs nothing, but the second outputs "asdf" as expected. How would I assign the "bar" key in the nested variable map to a value?

like image 868
Jeff Avatar asked Jun 18 '12 20:06

Jeff


4 Answers

When qvariant_cast is executed, object is copied. you should use a pointer of the object. You can try the following code instead of qvariant_cast code.

QVariantMap* m = (QVariantMap*)(map["foo"].data());
(*m)["bar"] = "a";
like image 149
Gilbert Lee Avatar answered Nov 04 '22 19:11

Gilbert Lee


Or you could do it the way the trollies don't like.

#include <QtCore/QCoreApplication>
#include <QVariant>
#include <QtDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QVariantMap map;
    map["foo"] = QVariant(QVariantMap());
    map["baz"] = "asdf";
    static_cast<QVariantMap>(map["foo"].data_ptr())["bar"] = "a";

    qDebug() << qvariant_cast<QVariantMap>(map["foo"])["bar"].toString();
    qDebug() << map["baz"].toString();

    return a.exec();
}

Or you could do it all safe and nice and use a QExplicitlySharedDataPointer instead of directly a QVariantMap. Like this:

#include <QtCore>
#include <QtDebug>
class VarMap : public QVariantMap, public QSharedData {};
typedef QExplicitlySharedDataPointer<VarMap> SharedVarMap;
Q_DECLARE_METATYPE(SharedVarMap)
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QVariantMap map;
    map["foo"] = SharedVarMap(new VarMap());
    map["baz"] = "asdf";
    map["foo"].value<SharedVarMap>()->["bar"] = "a";

    qDebug() << map["foo"].value<SharedVarMap>()->["bar"].toString();
    qDebug() << map["baz"].toString();

    return a.exec();
}
like image 43
Joren Boulanger Avatar answered Nov 04 '22 19:11

Joren Boulanger


The issue is that qvariant_cast doesn't return a reference to the internals of the QVariant that it is operating on; it returns a copy. As such, if you overwrite the "foo" element in your top-level map with a new child map, the code will work properly:

#include <QtCore/QCoreApplication>
#include <QVariant>
#include <QtDebug>

int main(int argc, char** argv)
{
    QCoreApplication a(argc, argv);
    QVariantMap map;
    map["foo"] = QVariant(QVariantMap());
    map["baz"] = "asdf";

    QVariantMap newMap;
    newMap["bar"] = "a";
    map["foo"] = QVariant(newMap);

    qDebug() << qvariant_cast<QVariantMap>(map["foo"])["bar"].toString();
    qDebug() << map["baz"].toString();

    return a.exec();
}

Presumably, you want to modify the existing map instead of overwritting it. You can accomplish this by copying the existing map, adding the new data (which will result in a deep copy), and then writing the map back in:

QVariantMap existingMap = qvariant_cast<QVariantMap>(map["foo"]);
existingMap["bar"] = "a";
map["foo"] = QVariant(existingMap);

If you're considering storing a large amount of data, you may wish to reconsider your use of QVariant.

like image 12
RA. Avatar answered Nov 04 '22 18:11

RA.


template <typename T>
inline T& getStoredValueRef(QVariant &v)
{
    const auto type = qMetaTypeId<T>(static_cast<T*>(nullptr));
    auto& d = v.data_ptr();
    if (type == d.type)
    {
        auto data = reinterpret_cast<T*>(d.is_shared ? d.data.shared->ptr : &d.data.ptr);
        return *data;
    }
    throw std::runtime_error("Bad type");
}

use it as

(getStoredValueRef<QVariantMap>(map["foo"]))["bar"] = "a";

and more deep

(getStoredValueRef<QVariantMap>(
    (getStoredValueRef<QVariantMap>(map["foo"]))["bar"]))["zoo"] = "a";
like image 2
esmirnov Avatar answered Nov 04 '22 18:11

esmirnov