我如何*直接*附加到存储在 QVariantMap 中的 QVariantList?

How can I *directly* append to a QVariantList, stored in a QVariantMap?

我有一个 QVariantMaps 集合,其中一些条目包含 QVariantLists。

我需要追加到列表中。这些列表可能会变得相当大,我必须对其中的许多列表执行此操作,每秒多次,基本上整个程序都是 运行。所以,效率在这里非常重要..

虽然我没有找到避免复制这些列表的方法!它们也可能是不可变的。 :(

这个有效:

map[ key ] = QVariantList() << map[ key ].toList() << someOtherVarList;

但是,这(在引擎盖下)做的工作比我想要的要多得多...

这是我试过的东西,但是 运行 撞墙了:

我可以通过使用我使用 QMutableMapIterator:

编写的这个宏来获得指向我想直接修改的变体的非常量指针
#define getMapValuePtr( keyType, valueType, map, keyValue, ptrName ) \
    valueType *ptrName( nullptr ); \
    for( QMap<keyType, valueType>::iterator it = map.begin(); \
         it != map.end(); ++it ) \
        { if( it.key()==keyValue ) { ptrName = &(*it); break; } }

示例:

const QString key( "mylist" );
QVariantMap map;
map[ key ] = QVariantList( {"a","b","c"} );           
getMapValuePtr( QString, QVariant, map, key, valuePtr )

所以现在 valuePtr 可以让我直接修改地图中的值,但它是一个 QVariant 的事实仍然存在...

我接下来尝试了以下操作,但 Qt Creator 没有突出显示任何内容以指示错误,但尽管如此,它还是无法完全编译...

QVariantList *valListPtr( qvariant_cast<QVariantList *>(*valuePtr) );    
valListPtr->append( QVariantList( {"x","y","z"} ) );

想法?

注意:并非这些地图中的所有条目都是列表。因此我不能将我的地图类型更改为 QMap<QString,QVariantList>。我试过了,许多客户 类 都对这段代码感到满意……如果它起作用的话,通过隐含地消除这个最后的症结点,这会简化事情。

知道了!

我在正确的轨道上,但必须深入研究 Qt 源代码才能弄清楚如何完成它。我现在有另一个宏可以调用,它将带我完成剩下的工作:

#define varPtrToListPtr( varPtr, ptrName ) \
    QVariantList *ptrName( varPtr->type() == QVariant::List ? \
        static_cast<QVariantList *>( static_cast<void *>( &(varPtr->d.data.c) ) ) \
        : nullptr );

因此,这是上面的示例,并填充了故事的其余部分:

const QString key( "mylist" );
QVariantMap map;
map[ key ] = QVariantList( {"a","b","c"} );           
getMapValuePtr( QString, QVariant, map, key, valuePtr )
if( !valuePtr ) return; // imagine this being inside a function...
varPtrToListPtr( valuePtr, valueListPtr )
if( valueListPtr ) *valueListPtr << QVariantList( {"x","y","z"} );

这实现了与我在问题中展示的直接 one-liner 相同的结果,但现在我避免了创建和销毁巨型列表的临时副本的所有开销!

诚然,与使用 QMap::value( key ) 相比,使用迭代器进行初始搜索可能会耗费一些时间,但是我这里的 none 地图有很多 k/v 对,因此总体收益避免列表副本否定了这一点。

更新:

  • 我 运行 使用此方法与原始方法进行了一些测试,并观察到它确实节省了相同工作负载所需时间的 99% 以上。就像在 50 毫秒内完成一项任务,而不是旧方法所需要的痛苦的 7.5 秒。

  • 这最初是在 Windows 上针对 MSVC 编写的。然而,我发现,在其他 platforms/compilers 上,除非您对 Qt 基础库进行 1 行调整,否则无法构建:

    • 打开 class QVariant 的 Qt 头文件。
    • 在文件中搜索包含以下内容的行:Private d;
    • 在其正上方添加行:public: // HACK!。这使其他构建上下文与对该 class 成员的 MSVC 访问保持一致。

Note: I fully acknowledge the "bad form", and the downsides, to this solution... BUT the payoff may be worth it for you. It was for my use case.

I have a collection of QVariantMaps which contains QVariantLists in SOME of their entries.

I need to append to the lists.

不要。显然你对这些 QVariants 有一些模式,否则你不会知道它们是否是一个列表。您是否针对它的强类型模型进行操作,然后在最后一刻将其转换为 QVariant(或其他)

map[ key ] = QVariantList() << map[ key ].toList() << someOtherVarList;

这就是将每个元素至少复制 两次 ,并查找变体两次,因此您可以通过将其拖放到

来立即削减 50%
auto & var = map[ key ]; 
var = (var.toList() << someOtherVarList);

主要问题是 QVariant 对性能来说是垃圾。对比 std::any 可以让您对存储的值进行引用访问。