[Development] What's Q_PRIMITIVE_TYPE for?

Giuseppe D'Angelo giuseppe.dangelo at kdab.com
Wed Nov 11 19:14:26 CET 2020


Hi,

On 11/11/2020 18:14, Thiago Macieira wrote:
> 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.

The problem is that ~100% of our value classes are not trivial, because 
we always initialize our data members. So, we need type traits anyhow to 
distinguish between primitive/relocatable/complex; and I am against 
calling it "Q_TRIVIAL_TYPE" because this property has now nothing to do 
with pure triviality.

*Some* trivial types can be initialized via memset(0), but not all of 
them, so the set of primitive types (according to our current 
definition) and the set trivial types are intersecting (*).

In theory we could just rely on the optimizer to turn 
std::uninitialized_value_construct_n into a memset(0). (If you have an 
out of line constructor that does 0-bit initialization, and the compiler 
doesn't see it and do the transformation, you don't have my sympathies.)

This would, in principle, allow for unifying handling of primitive and 
relocatable types:

* Construction: use uninitialized_value_construct
   * Primitive: the compiler figures out it's a memset()
   * Relocatable: call the default constructor (and possibly the 
compiler figures out it's a memset())

* Copy: just use std::uninitialized_copy
   * Primitive: the compiler figures out it's a memcpy()
   * Relocatable: call the copy constructor (possibly the compiler 
figures out it's a memcpy())

* Move: just use std::uninitialized_copy
   * Primitive: the compiler figures out it's a memcpy()
   * Relocatable: call the move constructor (possibly the compiler 
figures out it's a memcpy())

* Relocation (destructive move): use memcpy
   * Primitive: we know it works
   * Relocatable: we know it works

* Destruction: just use std::destroy
   * Primitive: compiler does nothing
   * Relocatable: call the destructors (possibly do nothing if trivial)


But as far as I can tell, compilers do not do these transformations as 
aggressively as we'd like. So we still have a distinct advantage at 
using the trait, at least for the Qt 6 lifetime. Take for instance 
QStringView:

> https://gcc.godbolt.org/z/6Taoo4

GCC, ICC, MSVC don't optimize anything. Clang chokes on the 
(pointer,int) scenario, but only if the initialization goes through a 
constructor. Don't ask me why.




(*) Properly intersecting, in the sense that here are primitive types 
that are not trivial (all/most the Q_PRIMITIVE_TYPE in Qt), and trivial 
types that are not primitive (the aforementioned class with pointers to 
data members).

My 2 c,
-- 
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

-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 4329 bytes
Desc: S/MIME Cryptographic Signature
URL: <http://lists.qt-project.org/pipermail/development/attachments/20201111/7ea3d134/attachment.bin>


More information about the Development mailing list