[Development] What's Q_PRIMITIVE_TYPE for?

Lars Knoll lars.knoll at qt.io
Mon Nov 9 11:33:32 CET 2020



> On 9 Nov 2020, at 10:25, Giuseppe D'Angelo via Development <development at qt-project.org> wrote:
> 
> Hi,
> 
> I have been wondering whether Q_PRIMITIVE_TYPE could be phased out and its support removed (kept for SC, but effectively it simply becomes synonymous for Q_MOVABLE_TYPE).
> 
> First and foremost, there seems to be disagreement on what "primitive type" means. From the docs:
> 
>> Q_PRIMITIVE_TYPE specifies that Type is a POD (plain old data) type with no constructor or destructor, or else a type where every bit pattern is a valid object and memcpy() creates a valid independent copy of the object.
> 
> With this definition in mind, why is QUuid primitive, QVector3D primitive, but QSize not primitive? They all are non-trivial types, they all have default constructors that set their contents to 0. Which one is correct? Can the others be changed while keeping BC?

If a type is relocatable, it’s default constructor memsets all bits of the object to 0, the copy constructor just does a memcpy of those bits and the destructor does nothing you can just as well mark the type as primitive. 

I think the fact that QSize is not marked primitive is a bug and should be fixed. Fortunately, changing that does not affect ABI.
> 
> Then: is this definition actually exploited anywhere?

It’s being used to select the QPodArrayOps for QList. The advantage it that we in this case use memset() instead of constructors, and memcpy/memmove to copy or move data. 

This does still make a difference compared to Q_RELOCATABLE_TYPE, where we need to use a combination of memmove and copy constructors, and also need to take care of at least basic exception safety when constructing items in the list. For primitive types we can avoid both copy constructors and the rollback handling for potential exceptions.

> 
> * Regarding construction: containers are required to value-initialize their contents (e.g. when resizing), so the optimization of "does not have a constructor" is not necessary at all. Is the reason why we carry Q_PRIMITIVE_TYPE that we plan to add functions to manipulate containers that do not initialize primitive types instead? (*)
> 
> * Regarding destruction: right now containers do use the primitive trait only with one purpose: to avoid calls to a type's destructor. Is such an optimization even still needed? Types with an empty destructor (trivial, or inline and empty, etc.) already emit no code on GCC / Clang under -Og (the default for debug builds), and no code under any compiler under -O2 (release builds). GCC even removes calls to library functions such as std::destroy_n under -Og.
> 
> (So, something we could already do is just call the destructor unconditionally, removing some unnecessary complexity from our containers.)
> 
> 
> The only other place that cares about primitive types is QMetaType, which uses this information to set the "NeedsConstruction" or "NeedsDestruction" type flags. But these type flags are never used anywhere inside of Qt; QMetaType value/copy/move constructs, and destructs, every type.

This would actually be something to look into. We could avoid creating those methods, and instead have a flag and in those cases the implementation in QMetaType could simply use memset and memcpy.

Cheers,
Lars

> 
> 
> (*) To see this in another way: it's an interesting approach to have types for which default construction does initialize, but also Qt specific "library" way to create uninitialized objects (in Qt containers).
> 
> 
> Thanks,
> -- 
> Giuseppe D'Angelo | giuseppe.dangelo at kdab.com | Senior Software Engineer
> KDAB (France) S.A.S., a KDAB Group company
> Tel. France +33 (0)4 90 84 08 53, http://www.kdab.com
> KDAB - The Qt, C++ and OpenGL Experts
> 
> 
> _______________________________________________
> Development mailing list
> Development at qt-project.org
> https://lists.qt-project.org/listinfo/development



More information about the Development mailing list