Re: [Development] Meaning of Q_PRIMITIVE_TYPE?

41 views
Skip to first unread message

Thiago Macieira

unread,
Mar 28, 2012, 1:06:46 PM3/28/12
to devel...@qt-project.org
On quarta-feira, 28 de março de 2012 15.37.46, Marc Mutz wrote:
> So I'd like to propose to extend the scope of Q_PRIMITIVE_TYPE to cover such
> non-POD classes that are nevertheless close enough to PODs.
>
> That means:
> 1a. memset(0, &t, sizeof(T)) constructs a valid object, and that object is
> equal to T(), if T has a default constructor.

That crashes, but I'm sure you meant well :-)

> -alternatively-
> 1b Any bit pattern represents a valid object (one that the dtor can be
> called on).
> 2. memcpy() creates a valid independent copy of the object
> 3. The dtor doesn't need to be run.
>
> With 1a+2+3, primitiveness is a strict superset of PODness.
>
> If Jędrzej is right, though, and the intent was for primitive types to not
> require initialisation, ie. the 1b alternative is used, PODness is no longer
> sufficient for primitiveness. And cups_option_t must be downgraded to
> movable.
>
> Looking at existing usage, I think 1a+2+3 comes closest to what everyone
> thinks primitive types mean, so I'd like to update the docs accordingly.
>
> Opinions?

Note that #2 is the difference between those simple types and the regular,
movable types. QString is movable, but a memcpy does not create a new
independent object. With that, the traits that I think are interesting to us
are:

- trivial default constructor ( = can be initialised by memset)
- trivially copyable ( = memcpy creates independent object)
[note: implies the next two]
- movable ( = memcpy can be performed if the old object is discarded) - not a
C++11 definition
- trivial destructor ( = no destructor need be run)

We currently only care about two combinations:

- Q_PRIMITIVE_TYPE = all of them
- Q_MOVABLE_TYPE = just the trivially-movable trait

Note that when all four traits are grouped together, C++11 defines them as
"trivial type".

The only thing I ask is that we keep Q_PRIMITIVE_TYPE for *real* primitive
types and create a new macro, say Q_TRIVIAL_TYPE, for everything that matches
the constraints but is not an actual primitive (fundamental). I don't know if
we'll ever need the difference, but I'd rather we had it proper if it doesn't
cost us much.

The closest definition that C++11 has for our Q_PRIMITIVE_TYPE is either
"scalar type" (arithmetic, enums, pointers) or "fundamental type" (arithmetic,
void).

--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Intel Sweden AB - Registration Number: 556189-6027
Knarrarnäsgatan 15, 164 40 Kista, Stockholm, Sweden

signature.asc

lars....@nokia.com

unread,
Mar 28, 2012, 3:06:38 PM3/28/12
to thiago....@intel.com, devel...@qt-project.org
On 3/28/12 7:06 PM, "ext Thiago Macieira" <thiago....@intel.com>
wrote:
Not sure it's worth differentiating here, and we're actually using
Q_PRIMITIVE_TYPE today for types that aren't real primitives. I'd simply
continue using Q_PRIMITIVE_TYPE as we did, ie. for classes matching the 4
conditions above.

Cheers,
Lars


>
>The closest definition that C++11 has for our Q_PRIMITIVE_TYPE is either
>"scalar type" (arithmetic, enums, pointers) or "fundamental type"
>(arithmetic,
>void).
>
>--
>Thiago Macieira - thiago.macieira (AT) intel.com
> Software Architect - Intel Open Source Technology Center
> Intel Sweden AB - Registration Number: 556189-6027
> Knarrarnäsgatan 15, 164 40 Kista, Stockholm, Sweden
>_______________________________________________
>Development mailing list
>Devel...@qt-project.org
>http://lists.qt-project.org/mailman/listinfo/development

_______________________________________________
Development mailing list
Devel...@qt-project.org
http://lists.qt-project.org/mailman/listinfo/development

Olivier Goffart

unread,
Mar 28, 2012, 4:02:04 PM3/28/12
to devel...@qt-project.org, Thiago Macieira
On Wednesday 28 March 2012 14:06:46 Thiago Macieira wrote:

> The only thing I ask is that we keep Q_PRIMITIVE_TYPE for *real* primitive
> types

Why?
Why would we need that.

If a type behaves like a POD, then it can be used as a POD.

Some types have default constructor to initialize to some default value. But
the constructor do not really need to be run.
And if it is not, it is left as uninitialized.

But it is fine.

Marc Mutz

unread,
Mar 29, 2012, 3:36:20 AM3/29/12
to devel...@qt-project.org, Thiago Macieira
On Wednesday March 28 2012, Thiago Macieira wrote:
>  - trivial default constructor ( = can be initialised by memset)
>  - trivially copyable ( = memcpy creates independent object)
>    [note: implies the next two]
>  - movable ( = memcpy can be performed if the old object is discarded) -
> not a C++11 definition
>  - trivial destructor ( = no destructor need be run)
>
> We currently only care about two combinations:
>
>  - Q_PRIMITIVE_TYPE = all of them
>  - Q_MOVABLE_TYPE = just the trivially-movable trait

"Trivial foo" in C++11 bans user-defined foo outright (ex: [class.ctor]/5).
Thus, Q_MOVABLE_TYPE should not mean C++-trivially-movable, because QString
(say) isn't trivially movable in the C++11 sense (has user-defined move
assignment operator).

We cannot use the C++11 terms, they're counter to existing QTypeInfo practice.
Q_MOVABLE_TYPE is equivalent to eastl::has_trivial_relocate, though nothing
of that sort exists in C++11 (yet).

> Note that when all four traits are grouped together, C++11 defines them as
> "trivial type".

And a trivial type that is standard-layout is a C++11-POD.

Thanks,
Marc

--
Marc Mutz <marc...@kdab.com> | Senior Software Engineer
KDAB (Deutschland) GmbH & Co.KG, a KDAB Group Company
www.kdab.com || Germany +49-30-521325470 || Sweden (HQ) +46-563-540090
KDAB - Qt Experts - Platform-Independent Software Solutions

Jedrzej Nowacki

unread,
Mar 30, 2012, 7:06:42 AM3/30/12
to devel...@qt-project.org

On Wednesday 28. March 2012 15.37.46 ext Marc Mutz wrote:

> Hi,

>

> Over at http://codereview.qt-project.org/21518, we're discussing whether

> QUuid is Q_PRIMITIVE_TYPE or only Q_MOVABLE_TYPE.

>

> The documentation of Q_DECLARE_TYPEINFO says Q_PRIMITIVE_TYPE means the

> type is a POD, without constructors or destructors. According to that

> definition, QUuid is not primitive. But neither would be QFixed and

> QFlags, which have been marked primitive in the past.

>

> I think the intention of Q_PRIMITIVE_TYPE, and the way it is used in Qt, is

> to mark types that act as ints do, ie. there's no class invariant, so

> char[sizeof(type)] = { 0 } is equal to the default-constructed value,

> uninitialised memory represents a valid value[1], and the object owns no

> resources. It is in this sense that QFixed, QFlags and QUuid are primitive

> types.

>

> [1] This doesn't hold for cups_option_t, btw, which is marked primitive,

> but is a struct{char*a,*b;}, so uninitialised memory does not represent a

> valid value, even though, technically, it's a POD.

>

> So I'd like to propose to extend the scope of Q_PRIMITIVE_TYPE to cover

> such non-POD classes that are nevertheless close enough to PODs.

>

> That means:

> 1a. memset(0, &t, sizeof(T)) constructs a valid object, and that object is

> equal to T(), if T has a default constructor.

> -alternatively-

> 1b Any bit pattern represents a valid object (one that the dtor can be

> called on).

> 2. memcpy() creates a valid independent copy of the object

> 3. The dtor doesn't need to be run.

>

> With 1a+2+3, primitiveness is a strict superset of PODness.

>

> If Jędrzej is right, though, and the intent was for primitive types to not

> require initialisation, ie. the 1b alternative is used, PODness is no

> longer sufficient for primitiveness. And cups_option_t must be downgraded

> to movable.

>

> Looking at existing usage, I think 1a+2+3 comes closest to what everyone

> thinks primitive types mean, so I'd like to update the docs accordingly.

>

> Opinions?

>

> Thanks,

> Marc

 

Hi,

 

I think that intent for primitive types was to mark POD types as docs say.

 

By removing '1b' requirement you will break or slowdown code that relays on that feature. For example:

- QVarLengthArray - checkout docs for resize() -> not fixable, you need to accept slowdown (memset call)

- QVariant - qvariant_p.h isNull detection -> fixable

 

The main problem appears when a "default" constructed value or uninitialized value has to be created.

 

As I said on the gerrit change, what you really need is something like canBeConstructedByMemsetItToZero :-)

 

Cheers,

 

Jędrek

Marc Mutz

unread,
Mar 30, 2012, 7:54:06 AM3/30/12
to devel...@qt-project.org

Thanks for the QVarLengthArray use-case. Yes, that requires 1b.

I don't mind 1b. Both 1a and 1b definitions make QUuid primitive (which is
where this discussion started). Since pointers are primitive and thus not
initialised by QVarLengthArray, I'd be ok even with cups_option_t (pair of
pointers) staying primitive.

So, does that mean you'll retract your -1?

Jedrzej Nowacki

unread,
Mar 30, 2012, 10:54:09 AM3/30/12
to ext Marc Mutz, devel...@qt-project.org

Then the only real problem I see is:

 

QVarLengthArray<QUuid> array;

array.resize(1);

 

currently this is always true:

array.at(0).isNull() == QUuid().isNull()

 

with new definition it may be something else. It is not big deal, just is a bit unintuitive but if you take QFlags instead QUuid then it may break existing code in a nasty way, for example:

QVarLengthArray<QFlags> array(1);

...

array[0] |= MyFlag;

 

How an user of QFlags can know if it is marked as primitive or not? The class has a ctor so from c++ perspective it is not a POD. Should he check source code? We do not have documentation for such things and searching for QTypeInfo specialization is not an option (it may placed everywhere and hidden in a macro). Dumping values of QTypeInfo<QFlags>, which is internal, is not nice. So how can he be safe in this case? Only by reinitializing values again:

QVarLengthArray<QFlags> array(1);

array[0] = QFlags();

...

array[0] |= MyFlag;

So I'm afraid that marking QFlags as primitive may decrease performance in global picture. I believe that it is not a nice API. I will keep my -1 because it is what I think about it. In the end, you can always override it, I'm fine with that ;-)

 

Cheers,

Jędrek

 

Thiago Macieira

unread,
Mar 30, 2012, 11:23:59 AM3/30/12
to devel...@qt-project.org
On sexta-feira, 30 de março de 2012 16.54.09, Jedrzej Nowacki wrote:
> Then the only real problem I see is:
>
> QVarLengthArray<QUuid> array;
> array.resize(1);
>
> currently this is always true:
> array.at(0).isNull() == QUuid().isNull()
>
> with new definition it may be something else. It is not big deal, just is a
> bit unintuitive but if you take QFlags instead QUuid then it may break
> existing code in a nasty way, for example:
> QVarLengthArray<QFlags> array(1);
> ...
> array[0] |= MyFlag;

How about this one:

QVarLengthArray<int> array(1);
array[0] |= 0x80;

?

> How an user of QFlags can know if it is marked as primitive or not? The

Use of QVarLengthArray should *only* be done with primitive types, the fixes
applied to it during Qt 4.x lifetime notwithstanding. So the user must know
that before he uses that class. If he's not sure, use QVector.

Also, QVarLengthArray should zero-initialise, but it doesn't.

> So I'm afraid that marking QFlags as primitive may decrease performance in
> global picture. I believe that it is not a nice API. I will keep my -1
> because it is what I think about it. In the end, you can always override
> it, I'm fine with that ;-)

How would it decrease performance?

signature.asc

Olivier Goffart

unread,
Mar 30, 2012, 12:41:46 PM3/30/12
to devel...@qt-project.org, Thiago Macieira
On Friday 30 March 2012 12:23:59 Thiago Macieira wrote:

> Use of QVarLengthArray should *only* be done with primitive types, the fixes
> applied to it during Qt 4.x lifetime notwithstanding. So the user must know
> that before he uses that class. If he's not sure, use QVector.

That is wrong. QVarLenghtArray can be use with every types.

> Also, QVarLengthArray should zero-initialise, but it doesn't.

It is documented not to.

But yes, it probably should. (we can add a Qt::Uninitialized overload if we
need it)

Thiago Macieira

unread,
Mar 30, 2012, 2:35:40 PM3/30/12
to devel...@qt-project.org
On sexta-feira, 30 de março de 2012 18.41.46, Olivier Goffart wrote:
> On Friday 30 March 2012 12:23:59 Thiago Macieira wrote:
> > Use of QVarLengthArray should *only* be done with primitive types, the
> > fixes applied to it during Qt 4.x lifetime notwithstanding. So the user
> > must know that before he uses that class. If he's not sure, use QVector.
>
> That is wrong. QVarLenghtArray can be use with every types.

That statement ^^^

> > Also, QVarLengthArray should zero-initialise, but it doesn't.
>
> It is documented not to.

And this statement ^^^

Are in contradiction. If it doesn't zero-initialise, then it doesn't
initialise. Then using it for non-PODs is dangerous.

If it supports any type, then it must initialise them all.

> But yes, it probably should. (we can add a Qt::Uninitialized overload if we
> need it)

I'd rather we removed support for non-primitives. The class wasn't designed
for that.

signature.asc

Olivier Goffart

unread,
Mar 31, 2012, 4:19:08 AM3/31/12
to devel...@qt-project.org, Thiago Macieira
On Friday 30 March 2012 15:35:40 Thiago Macieira wrote:
> On sexta-feira, 30 de março de 2012 18.41.46, Olivier Goffart wrote:
> > On Friday 30 March 2012 12:23:59 Thiago Macieira wrote:
> > > Use of QVarLengthArray should *only* be done with primitive types, the
> > > fixes applied to it during Qt 4.x lifetime notwithstanding. So the user
> > > must know that before he uses that class. If he's not sure, use QVector.
> >
> > That is wrong. QVarLenghtArray can be use with every types.
>
> That statement ^^^
>
> > > Also, QVarLengthArray should zero-initialise, but it doesn't.
> >
> > It is documented not to.
>
> And this statement ^^^
>
> Are in contradiction. If it doesn't zero-initialise, then it doesn't
> initialise. Then using it for non-PODs is dangerous.
>
> If it supports any type, then it must initialise them all.


It initialize the types that are not marked as Q_PRIMITIVE_TYPE.
The documentation even says that it is only primitive types that are not
initialized

>
> > But yes, it probably should. (we can add a Qt::Uninitialized overload if
> > we need it)
>
> I'd rather we removed support for non-primitives. The class wasn't designed
> for that.

It was designed for that, and works well.

lars....@nokia.com

unread,
Mar 31, 2012, 8:37:47 AM3/31/12
to oli...@woboq.com, devel...@qt-project.org, thiago....@intel.com
On 3/31/12 10:19 AM, "ext Olivier Goffart" <oli...@woboq.com> wrote:

>On Friday 30 March 2012 15:35:40 Thiago Macieira wrote:
>> On sexta-feira, 30 de março de 2012 18.41.46, Olivier Goffart wrote:
>> > On Friday 30 March 2012 12:23:59 Thiago Macieira wrote:
>> > > Use of QVarLengthArray should *only* be done with primitive types,
>>the
>> > > fixes applied to it during Qt 4.x lifetime notwithstanding. So the
>>user
>> > > must know that before he uses that class. If he's not sure, use
>>QVector.
>> >
>> > That is wrong. QVarLenghtArray can be use with every types.
>>
>> That statement ^^^
>>
>> > > Also, QVarLengthArray should zero-initialise, but it doesn't.
>> >
>> > It is documented not to.
>>
>> And this statement ^^^
>>
>> Are in contradiction. If it doesn't zero-initialise, then it doesn't
>> initialise. Then using it for non-PODs is dangerous.
>>
>> If it supports any type, then it must initialise them all.
>
>
>It initialize the types that are not marked as Q_PRIMITIVE_TYPE.
>The documentation even says that it is only primitive types that are not
>initialized

The problem is that users are not usually aware whether a class is
primitive or not.

>
>>
>> > But yes, it probably should. (we can add a Qt::Uninitialized
>>overload if
>> > we need it)
>>
>> I'd rather we removed support for non-primitives. The class wasn't
>>designed
>> for that.
>
>It was designed for that, and works well.

As the guy who wrote the first implementation: Originally it wasn't. It
was only there to help us place an array of primitive types on the stack.
It was basically there only to work around the fact that C++ doesn't allow
variable length arrays on the stack.


Cheers,
Lars

jedrzej...@nokia.com

unread,
Apr 3, 2012, 3:52:37 AM4/3/12
to lars....@nokia.com, devel...@qt-project.org
Hi,

> The problem is that users are not usually aware whether a class is
> primitive or not.

And with Q_PRIMITIVE_TYPE they have no way to check if a class is primitive or not, moreover it may be changed by us even in a minor releases. How bad it is?

Cheers,
Jędrek
________________________________________
From: development-bounces+jedrzej.nowacki=noki...@qt-project.org [development-bounces+jedrzej.nowacki=noki...@qt-project.org] on behalf of Knoll Lars (Nokia-MP/Oslo)
Sent: Saturday, March 31, 2012 2:37 PM
To: oli...@woboq.com; devel...@qt-project.org
Cc: thiago....@intel.com
Subject: Re: [Development] Meaning of Q_PRIMITIVE_TYPE?

jedrzej...@nokia.com

unread,
Apr 3, 2012, 4:02:53 AM4/3/12
to devel...@qt-project.org
Hi,

> How about this one:
> QVarLengthArray<int> array(1);
> array[0] |= 0x80;

This is a bit different because you can expect from users that they know about POD and in general about variable initialization in C++. QFlags, QUuid have default constructor, int hasn't.

> Also, QVarLengthArray should zero-initialise, but it doesn't.

I like this feature it allows to avoid double initialization in some cases, but maybe it is not the most intuitive behavior. Anyway it is a bit out of scope.

Cheers,

Jędrek

________________________________________
From: development-bounces+jedrzej.nowacki=noki...@qt-project.org [development-bounces+jedrzej.nowacki=noki...@qt-project.org] on behalf of ext Thiago Macieira [thiago....@intel.com]
Sent: Friday, March 30, 2012 5:23 PM
To: devel...@qt-project.org


Subject: Re: [Development] Meaning of Q_PRIMITIVE_TYPE?

On sexta-feira, 30 de março de 2012 16.54.09, Jedrzej Nowacki wrote:

How about this one:

?

Marc Mutz

unread,
Apr 3, 2012, 4:20:11 AM4/3/12
to devel...@qt-project.org
Hi,

On Tuesday April 3 2012, jedrzej...@nokia.com wrote:
[...]


> > Also, QVarLengthArray should zero-initialise, but it doesn't.
>
> I like this feature it allows to avoid double initialization in some cases,
> but maybe it is not the most intuitive behavior. Anyway it is a bit out of
> scope.

If you want to avoid double initialisation, the correct way is
reserve()+push_back(), not resize()+operator[]. That works even for complex
types.

Thanks,
Marc

--
Marc Mutz <marc...@kdab.com> | Senior Software Engineer
KDAB (Deutschland) GmbH & Co.KG, a KDAB Group Company
www.kdab.com || Germany +49-30-521325470 || Sweden (HQ) +46-563-540090
KDAB - Qt Experts - Platform-Independent Software Solutions

Thiago Macieira

unread,
Apr 3, 2012, 8:29:44 AM4/3/12
to devel...@qt-project.org
On terça-feira, 3 de abril de 2012 10.20.11, Marc Mutz wrote:
> Hi,
>
> On Tuesday April 3 2012, jedrzej...@nokia.com wrote:
> [...]
>
> > > Also, QVarLengthArray should zero-initialise, but it doesn't.
> >
> > I like this feature it allows to avoid double initialization in some
> > cases,
> > but maybe it is not the most intuitive behavior. Anyway it is a bit out of
> > scope.
>
> If you want to avoid double initialisation, the correct way is
> reserve()+push_back(), not resize()+operator[]. That works even for complex
> types.

The thing is that, like Lars said, QVarLengthArray was invented to replace the
C99 feature of variable-length arrays, hence the name. In C99 but not in C++98
or C++11, you can write:

char buffer[length];

where length is not a constant expression (to borrow a C++11 term). That buffer
is not initialised because char has no constructor.

So like Jędrzej says, the users expect that an array of chars is uninitialised
to garbage, but an array of QUuid is properly zeroed because this produces a
zero QUuid:

QUuid uuid;

My experience producing a type that doesn't initialise by default
(QElapsedTimer) is that users really don't expect C++ types to remain
uninitialised.

signature.asc
Reply all
Reply to author
Forward
0 new messages