Назначение вложенного 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();
}

Я пытаюсь назначить QVariant внутри вложенной QVariantMap. Первый qDebug() ничего не выводит, а второй выводит "asdf", как и ожидалось. Как бы присвоить ключу "бар" в вложенной карте переменных значение?

Ответ 1

Проблема в том, что qvariant_cast не возвращает ссылку на внутренности QVariant, на которой он работает; он возвращает копию. Таким образом, если вы перезапишите элемент "foo" на карте верхнего уровня с новой дочерней картой, код будет работать правильно:

#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();
}

Предположительно, вы хотите изменить существующую карту вместо ее записи. Вы можете выполнить это, скопировав существующую карту, добавив новые данные (что приведет к глубокой копии), а затем снова верните карту:

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

Если вы планируете хранить большой объем данных, вы можете пересмотреть свое использование QVariant.

Ответ 2

Или вы могли бы сделать это так, как тролли не нравятся.

#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();
}

Или вы можете сделать все это безопасным и приятным и использовать QExplicitlySharedDataPointer вместо прямого QVariantMap. Вот так:

#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();
}

Ответ 3

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");
}

используйте его как

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

и более глубоко

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

Ответ 4

Когда выполняется qvariant_cast, объект копируется. вы должны использовать указатель объекта. Вы можете попробовать следующий код вместо кода qvariant_cast.

QVariantMap* m = (QVariantMap*)(map["foo"].data());
(*m)["bar"] = "a";