[Development] What's Q_PRIMITIVE_TYPE for?

Thiago Macieira thiago.macieira at intel.com
Wed Nov 11 18:14:07 CET 2020


On Monday, 9 November 2020 08:16:51 PST Giuseppe D'Angelo via Development 
wrote:
> >> 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.
> To be honest, I don't see an advantage here, given QMetaType doesn't
> need to "bulk" construct or destroy N objects but only 1, and it could
> just call its constructor/destructor rather than short circuiting the
> operations to memset/memcpy.

If we don't need the memset-as-initialisation, we can relax the "primitive 
type" to an actual C++ category: trivially-copyable type. We're already 
renaming the Q_MOVABLE_TYPE category to align with future standard direction 
and calling it Q_RELOCATABLE_TYPE.

A trivially copyable class type has (http://eel.is/c++draft/class.prop#1):
 * a non-deleted, trivial copy constructor or move constructor
 * a non-deleted, trivial destructor

This differs from relocatability by requiring the trivial destructor, which 
means we can forego calling it in QMetaType (in containers I'd expect the loop 
simply expands to no code).

A trivially-copyable class need not have a trivial constructor (http://eel.is/
c++draft/class.default.ctor#3), but we can add that requirement too, which 
would make our requirement even stronger, at trivial type. Note that a trivial 
constructor does not imply the zero-initialised object is equivalent to the 
default construction type. This example from Peppe's other email is a trivial 
type and yet its default (constexpr) construction is not all zeroes:

struct C { int A::* ptr; };

see <https://gcc.godbolt.org/z/81dacG>.

In fact, I think pointer-to-member-object is the only example of a trivial 
construction to non-zero-bits in either ABI that matters. For everything else, 
a memset-to-zero construction will match the default (trivial, constexpr) 
constructor. And for PMOs, it will create a valid if non-default 
representation.

So my recommendation is:
 1) deprecate Q_PRIMITIVE_TYPE and rename to Q_TRIVIAL_TYPE
 2) *not* use memset-to-zero construction anywhere

#2 implies changing QPodArrayOps, which does use memset, to use a loop calling 
the default constructor. Two of the four compilers do optimise that into a 
call into memset: https://gcc.godbolt.org/z/Ks3M5h. And there's nothing the 
ICC team likes to work on more than losing on a benchmark.

-- 
Thiago Macieira - thiago.macieira (AT) intel.com
  Software Architect - Intel DPG Cloud Engineering





More information about the Development mailing list