Re: [Development] API style guide: scoped enum or not?

2023-06-14 Thread Thiago Macieira
On Wednesday, 14 June 2023 14:26:52 PDT Giuseppe D'Angelo via Development 
wrote:
> I think this should be a completely different question than the usage of
> scoped enums in new code. For instance, I'm all for scoped enums and
> against such refactoring.

The refactoring *could* be done now, but it's a massive code update and can't 
be helped with macros. It would increase the size of most headers and 
substantially so for qnamespace.h, which might impact compilation time.

Therefore, I oppose such a change now.

But I do support a statement of intent that in Qt 7 we want to revisit this 
and change where possible. It might be a macro-enabled backwards compatible 
API, or maybe we'll be able to use "using enum" by then.

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


smime.p7s
Description: S/MIME cryptographic signature
-- 
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development


Re: [Development] API style guide: scoped enum or not?

2023-06-14 Thread Giuseppe D'Angelo via Development

Il 14/06/23 22:15, Volker Hilsheimer via Development ha scritto:

-1 to B from me as well.

We can allow unscoped enum as an acceptable (if explained) exception from the 
rule of using scoped enums. Otherwise we remove a tool from our toolbox, even 
if it has it’s uses in certain (increasingly rare, perhaps) situations.

More importantly, I don’t agree with the footnoted proposal of making all 
currently unscoped enums scoped in Qt 7. Such a change would cause a massive 
porting effort (tool or not), with zero value for users. There might be 
exceptions where we have genuine issues from certain enums being unscoped, we 
can fix those with appropriate porting aids and compatibility wrappers/aliases.


I think this should be a completely different question than the usage of 
scoped enums in new code. For instance, I'm all for scoped enums and 
against such refactoring.


My 2 c,
--
Giuseppe D'Angelo | giuseppe.dang...@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



smime.p7s
Description: Firma crittografica S/MIME
-- 
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development


Re: [Development] API style guide: scoped enum or not?

2023-06-14 Thread Thiago Macieira
On Wednesday, 14 June 2023 12:49:22 PDT Allan Sandfeld Jensen wrote:
> As discussed earlier. Better naming in many cases until we can depend on
> C++20 in API.

There's nothing in C++20 that would allow us to design APIs differently.

You may be thinking of C++23 "using enum" feature, which is barely supported 
anywhere and doesn't help us in any way I can see. It might allow us in the 
future to change our existing unscoped enums to scoped, but until then there's 
little we ca do.

So this question is strictly a C++11 scoped enum one.

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


smime.p7s
Description: S/MIME cryptographic signature
-- 
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development


Re: [Development] API style guide: scoped enum or not?

2023-06-14 Thread Volker Hilsheimer via Development
+1 to A and C.

> On 14 Jun 2023, at 21:49, Allan Sandfeld Jensen  wrote:
> 
> On Mittwoch, 14. Juni 2023 17:48:27 CEST Thiago Macieira wrote:
>> On Wednesday, 14 June 2023 08:35:16 PDT Marc Mutz via Development wrote:
>> B) new enums MUST be scoped, also when nested in classes¹²
> 
> -1 Disagree
 
 -1 Disagree
>>> 
>>> Ok. But _why_? (Q to both)
>> 
>> I'm inclined to agree with (B), so I'd like to understand the objections.
> 
> As discussed earlier. Better naming in many cases until we can depend on
> C++20 in API. I disagree with designing API for standards we are not yet 
> allowed to use. And there are a few rare cases with flags or other composite 
> value types, where the implicit casting is desirable. I wouldn't mind saying 
> scoped enums are the preferred default, and any unscoped enum needs to be 
> justified in the commit message, but I would hate to ban them out right. 
> 
> Best regards
> Allan


-1 to B from me as well.

We can allow unscoped enum as an acceptable (if explained) exception from the 
rule of using scoped enums. Otherwise we remove a tool from our toolbox, even 
if it has it’s uses in certain (increasingly rare, perhaps) situations.

More importantly, I don’t agree with the footnoted proposal of making all 
currently unscoped enums scoped in Qt 7. Such a change would cause a massive 
porting effort (tool or not), with zero value for users. There might be 
exceptions where we have genuine issues from certain enums being unscoped, we 
can fix those with appropriate porting aids and compatibility wrappers/aliases.

Volker

-- 
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development


Re: [Development] API style guide: scoped enum or not?

2023-06-14 Thread Allan Sandfeld Jensen
On Mittwoch, 14. Juni 2023 17:48:27 CEST Thiago Macieira wrote:
> On Wednesday, 14 June 2023 08:35:16 PDT Marc Mutz via Development wrote:
> > >>> B) new enums MUST be scoped, also when nested in classes¹²
> > >> 
> > >> -1 Disagree
> > > 
> > > -1 Disagree
> > 
> > Ok. But _why_? (Q to both)
> 
> I'm inclined to agree with (B), so I'd like to understand the objections.

As discussed earlier. Better naming in many cases until we can depend on
C++20 in API. I disagree with designing API for standards we are not yet 
allowed to use. And there are a few rare cases with flags or other composite 
value types, where the implicit casting is desirable. I wouldn't mind saying 
scoped enums are the preferred default, and any unscoped enum needs to be 
justified in the commit message, but I would hate to ban them out right. 

Best regards
Allan




-- 
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development


Re: [Development] C++20 comparisons @ Qt (was: Re: C++20 @ Qt)

2023-06-14 Thread Thiago Macieira
On Wednesday, 14 June 2023 01:52:49 PDT Marc Mutz via Development wrote:
> == Why E _and_ O? ==
> 
> The reason we need E _and_ O for ordered types instead of just O is that
> O needs to order the lhs w.r.t. the rhs, which generally involves
> looking at all the state of the objects whereas E just needs to find
> _one_ difference to be able to quickly return false. E.g., a container
> can do
> 
>  bool E(~~~ lhs, ~~~ rhs) {
>  if (lhs.size() != rhs.size()) return false; // O cannot do this!
>  
>  }
> 
> This is a very important optimization (and one reason why L1 string
> literals are going to stay and not be replaced with UTF-8 ones: L1 op
> UTF-16 _can_ use the size() short-cut while UTF-8 op UTF-16 cannot).

It's also why glibc added __memcmpeq and GCC and Clang can generate a call to 
that. Same reason why I added QtPrivate::equalStrings() different from 
QtPrivate::compareStrings() (internally they call ucstreq and ucstrcmp, 
respectively).

> == Can E be spelled op==? ==
> 
> It could. However, op== cannot have extra arguments and we have at least
> one use-case where E might have an additional argument: case-insensitive
> string comparisons. We currently have no (public) API to express op==,
> but case-insensitively. The closest we have is QString::compare(), but
> that is an O, so it cannot use the size() mismatch shortcut, assuming
> it's valid for case-insensitive string comparison (it is for L1, dunno
> about UTF-16).

Hmm... the facts are correct but I don't think that leads to the conclusion. 
The whole objective here is to provide op== anyway, so the fact that some 
classes have this implemented in another method that can do more should be 
irrelevant. For those, this op== can be inline and call the out-of-line 
function that does the relevant work.

> There's also the situation where (cheap!) implicit conversions allow one
> E to backfill several different op== (which, being hidden friends, don't
> participate in implicit conversions themselves). If E is spelled op==,
> what would the macros use to implement op==? It would be up to the class
> author to supply sufficiently-many op== in the correct form. We don't
> want that. We want all op== to be generated from the macros so we have
> central control over their generation (providing reversed operators in
> C++17, getting the signatures right, noexcept-ness, hidden-friend-ness,
> ...).

They can be exceptions and written manually.

However, I do not object to having the op== be an inline hidden friend, 
calling a private static noexcept equality comparator function (whether that's 
inline or not, taking extra arguments or not). In fact, I like that, purely on 
stylistic reasons.

> == O as public API ==
>
> In C++20, O would be spelled op<=>, but this is not possible in C++17,
> so O needs to be an actual named function, not an operator. The
> question, and it's a rhetorical one, then becomes: should O be public
> API, and the (rhetorical) answer is "yes, because C++17 users will want
> to be able to access O in C++17 projects, too (in C++20, they could call
> op<=>)". This includes Qt's own implementation of O's.

Side note: I think we should start compiling Qt with C++20 right now, for all 
compilers that support it, with no possibility for the user to turn that off. 
There is C++20 functionality we could use in our implementations that don't 
affect ABI and don't constrain C++17 users. And I also don't object to 
providing C++20-only features, discussed on a case-by-case basis.

> Applied to our situation, this means:
> 
> - each (orderable) class supplies O as a hidden friend
> - may supply it as a (non-static) member function

"At a minimum" as a hidden friend. It may be public API, like 
QString::compare(), whether static or non-static.

> And we have the calling convention:
> lhs.O(rhs);
> Qt::O(lhs, rhs); // if you know the type
> 
> using Qt::O;
> O(lhs, rhs);
> . // if you don't

I don't think this is necessary. I would prefer if that were only allowed 
through op<=>.

C++17-constrained users would therefore need to know the types in question, 
know what the "O" function is called and how to call it (static vs non-
static). And if it isn't public at all, then you're limited to the traditional 
relational operators (op<, op<=, op> and op>=). Mind you, we're not breaking 
those users in any way; we're simply not adding extra API for them.

Paraphrasing you about a decade ago, "C++17 costs more".

> This leads to the finding that O (and, depending on the outcome of the
> first two Open Questions) E cannot be named like static binary functions
> in existing classes, as, even if they are semantically compatible,
> they're syntactically incompatible.
>
> That objectively rules out "compare" for O and "equals" for E.

Conclusion invalidated due to premise rejected. I couldn't follow your logic 
anyway, but since the premise was no longer applicable, I didn't try very 
hard.

> == 

Re: [Development] API style guide: scoped enum or not?

2023-06-14 Thread Tor Arne Vestbø via Development


> On 14 Jun 2023, at 17:48, Thiago Macieira  wrote:
> 
> On Wednesday, 14 June 2023 08:35:16 PDT Marc Mutz via Development wrote:
> B) new enums MUST be scoped, also when nested in classes¹²
 
 -1 Disagree
>>> 
>>> -1 Disagree
>> 
>> Ok. But _why_? (Q to both)
> 
> I'm inclined to agree with (B), so I'd like to understand the objections.

This position has been explained ad nauseam. Please go back and read earlier 
emails in this thread.

Cheers,
Tor Arne
-- 
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development


Re: [Development] C++20 comparisons @ Qt (was: Re: C++20 @ Qt)

2023-06-14 Thread Edward Welbourne via Development
Marc Mutz (14 June 2023 10:52) wrote:
> == Naming E ==
>
> So far, we've been using equal(). equals() doesn't work for technical
> reasons, but while it'd work as a member function lhs.equals(rhs),
> it's also kinda wrong if the function is taking two arguments
> (equals(lhs, rhs), but there are _two_ objects). So equal() as the
> plural form or equals() makes sense.

No, it really doesn't (but it's illuminating to finally see why you
thought it did).

Sure, if we have two objects whose masses together equal that of a
third, you get "equal" as the plural of "equals" but it's *still
transitive* and its "those two equal this one".  You can stretch to
"these two equal one another" but it's severely contrived to do so; you
would say "these two are equal".  Besides, if you don't like areEqual(),
you're really not going to welcome equalOneAnother().

I've no objection to eq() and cmp(), though,

Eddy.
-- 
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development


Re: [Development] API style guide: scoped enum or not?

2023-06-14 Thread Thiago Macieira
On Wednesday, 14 June 2023 08:35:16 PDT Marc Mutz via Development wrote:
> >>> B) new enums MUST be scoped, also when nested in classes¹²
> >> 
> >> -1 Disagree
> > 
> > -1 Disagree
> 
> Ok. But _why_? (Q to both)

I'm inclined to agree with (B), so I'd like to understand the objections.

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


smime.p7s
Description: S/MIME cryptographic signature
-- 
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development


Re: [Development] API style guide: scoped enum or not?

2023-06-14 Thread Marc Mutz via Development
On 14.06.23 16:33, Tor Arne Vestbø via Development wrote:
>> On 14 Jun 2023, at 16:20, Allan Sandfeld Jensen  wrote:
[...]
>>> B) new enums MUST be scoped, also when nested in classes¹²
>> -1 Disagree
> 
> -1 Disagree

Ok. But _why_? (Q to both)

>>
>>>
>>> C) scoped enums SHOULD NOT repeat (part of) an enum's type name in the
>>> enumerators²
>>>
>> +1 Agree, and if you find you do repeat the type name in an unscoped 
>> enum it
>> probably should be scoped. But isn't this already in our style guide?
> 
> +1 Agree, and yes, it’s already in our style guide.

It's not really, no. The wording is that repeating the name isn't 
_necessary_. That's the kind of wording that will get a discussion going 
over every new enum. It should say you shouldn't repeat the name, unless 
there's a reason (and QML is not a reason, otherwise it's not an 
exception anymore).

-- 
Marc Mutz 
Principal Software Engineer

The Qt Company
Erich-Thilo-Str. 10 12489
Berlin, Germany
www.qt.io

Geschäftsführer: Mika Pälsi, Juha Varelius, Jouni Lintunen
Sitz der Gesellschaft: Berlin,
Registergericht: Amtsgericht Charlottenburg,
HRB 144331 B

-- 
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development


Re: [Development] API style guide: scoped enum or not?

2023-06-14 Thread Tor Arne Vestbø via Development


On 14 Jun 2023, at 16:20, Allan Sandfeld Jensen  wrote:

On Mittwoch, 14. Juni 2023 14:59:40 CEST Marc Mutz via Development wrote:
On 02.05.23 10:58, Volker Hilsheimer via Development wrote:
During the header review, but also in API discussions leading up to it, we
had a few cases where it would have helped if we had clearer guidelines
about when to use scoped enums, and when not.
Was there some consensus here? We're discussing this _again_ in the 6.6
review...

Let me propose the following, separate issues, so we have something
concrete to +1 or -1:

[All capitalized words have the meaning of IETF RFCs]

A) new enums MUST have an explicit underlying type¹²
+1 Sure

+1 Sure



B) new enums MUST be scoped, also when nested in classes¹²
-1 Disagree

-1 Disagree



C) scoped enums SHOULD NOT repeat (part of) an enum's type name in the
enumerators²

+1 Agree, and if you find you do repeat the type name in an unscoped enum it
probably should be scoped. But isn't this already in our style guide?

+1 Agree, and yes, it’s already in our style guide.

Cheers,
Tor Arne

-- 
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development


Re: [Development] API style guide: scoped enum or not?

2023-06-14 Thread Allan Sandfeld Jensen
On Mittwoch, 14. Juni 2023 14:59:40 CEST Marc Mutz via Development wrote:
> On 02.05.23 10:58, Volker Hilsheimer via Development wrote:
> > During the header review, but also in API discussions leading up to it, we
> > had a few cases where it would have helped if we had clearer guidelines
> > about when to use scoped enums, and when not.
> Was there some consensus here? We're discussing this _again_ in the 6.6
> review...
> 
> Let me propose the following, separate issues, so we have something
> concrete to +1 or -1:
> 
> [All capitalized words have the meaning of IETF RFCs]
> 
> A) new enums MUST have an explicit underlying type¹²
+1 Sure

> 
> B) new enums MUST be scoped, also when nested in classes¹²
-1 Disagree

> 
> C) scoped enums SHOULD NOT repeat (part of) an enum's type name in the
> enumerators²
> 
+1 Agree, and if you find you do repeat the type name in an unscoped enum it 
probably should be scoped. But isn't this already in our style guide?

Best regards
Allan



-- 
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development


Re: [Development] API style guide: scoped enum or not?

2023-06-14 Thread Marc Mutz via Development
On 02.05.23 10:58, Volker Hilsheimer via Development wrote:
> During the header review, but also in API discussions leading up to it, we 
> had a few cases where it would have helped if we had clearer guidelines about 
> when to use scoped enums, and when not.

Was there some consensus here? We're discussing this _again_ in the 6.6 
review...

Let me propose the following, separate issues, so we have something 
concrete to +1 or -1:

[All capitalized words have the meaning of IETF RFCs]

A) new enums MUST have an explicit underlying type¹²

For unscoped enums, the compiler otherwise picks one, possibly resulting 
in BiC when new addtions change the underlying type or otherwise just 
warnings because one compiler uses a signed and the other an unsigned 
type (most recent exmaple: QTBUG-113792). For scoped enums, the type is 
int, but probably not always, and why require the reader of the code to 
know the C++ rules when we can make it explicit?

B) new enums MUST be scoped, also when nested in classes¹²

Type safety > verbosity. Qt has never preferred brevity over verbosity 
when it comes to readability, and while the use of short identifiers 
makes code often more readable than using long ones, C++20's `using 
enum` feature empowers users to choose between brevity and verbosity on 
a per-use basis. Since we should be designing for the future and not the 
past, this removes the major objection for scoped enums. C++17 doesn't 
matter. If we really want leaking, we can use constexpr variables of 
scoped enum type in lieu of enumerators.

C) scoped enums SHOULD NOT repeat (part of) an enum's type name in the 
enumerators²

This was a work-around from the times of C/C++98 when you could neither 
scope the value of an enumeration nor prevent the leakage of enumerator 
names into the enclosing scope. We have both now in C++11: any 
enumerator can be scoped and scoped enums don't leak the name into the 
enclosing scope anymore.

I cannot rule out that sometimes some part of the enum's type may make 
sense in an enumerator's name, but not consistently in all of them just 
because one of them needs it etc, so this is a SHOULD, not a MUST.

I have been told today that QML allows scoped C++ enums to be used 
without the scope. If this is true, it should be fixed to always require 
the scope, because otherwise the argument goes "but in QML, the scope 
isn't visible, so a part of the enum type name must be included in the 
enumerators".

¹ except when C compatibility is required
² applies to all enums come Qt 7

It would be good if we could have clarity on A-C before we finish up the 
6.6 API review.

Thanks,
Marc

-- 
Marc Mutz 
Principal Software Engineer

The Qt Company
Erich-Thilo-Str. 10 12489
Berlin, Germany
www.qt.io

Geschäftsführer: Mika Pälsi, Juha Varelius, Jouni Lintunen
Sitz der Gesellschaft: Berlin,
Registergericht: Amtsgericht Charlottenburg,
HRB 144331 B

-- 
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development


Re: [Development] C++20 comparisons @ Qt (was: Re: C++20 @ Qt)

2023-06-14 Thread Marc Mutz via Development
On 03.11.22 10:40, Marc Mutz via Development wrote:
> TL;DR: provide named functions for the minimal set of op== and op<=>
> we'd have to write in C++20, then use macros to turn these into op==,
> op!=, op<, op>, op<=, op>= for C++17 builds and op== and op<=> for C++20
> builds.

This part somehow became lost and kinda made the whole feature miss the 
6.6 train, so let me expand on it a bit, and collect open questions 
(marked with → below):

We want the implementation of these relational operators be as close as 
possible to the way C++20 will work: There, you implement op== and op<=> 
and the compiler synthesizes everything else (cf. parent message for 
details). So we need one function E backing op== and, for ordered types, 
another function O backing op<=>.

== Why E _and_ O? ==

The reason we need E _and_ O for ordered types instead of just O is that 
O needs to order the lhs w.r.t. the rhs, which generally involves 
looking at all the state of the objects whereas E just needs to find 
_one_ difference to be able to quickly return false. E.g., a container 
can do

 bool E(~~~ lhs, ~~~ rhs) {
 if (lhs.size() != rhs.size()) return false; // O cannot do this!
 
 }

This is a very important optimization (and one reason why L1 string 
literals are going to stay and not be replaced with UTF-8 ones: L1 op 
UTF-16 _can_ use the size() short-cut while UTF-8 op UTF-16 cannot).

== Can E be spelled op==? ==

It could. However, op== cannot have extra arguments and we have at least 
one use-case where E might have an additional argument: case-insensitive 
string comparisons. We currently have no (public) API to express op==, 
but case-insensitively. The closest we have is QString::compare(), but 
that is an O, so it cannot use the size() mismatch shortcut, assuming 
it's valid for case-insensitive string comparison (it is for L1, dunno 
about UTF-16).

So there's a certain appeal in using E as a way to consistently spell 
"op== with extra arguments". We could, e.g. do E(lhs, rhs, 
Qt::FuzzyComparison) for anything involving FP.

There's also the situation where (cheap!) implicit conversions allow one 
E to backfill several different op== (which, being hidden friends, don't 
participate in implicit conversions themselves). If E is spelled op==, 
what would the macros use to implement op==? It would be up to the class 
author to supply sufficiently-many op== in the correct form. We don't 
want that. We want all op== to be generated from the macros so we have 
central control over their generation (providing reversed operators in 
C++17, getting the signatures right, noexcept-ness, hidden-friend-ness, 
...).

So I would require new information to accept anything than a "no" here:

→ Semi-Open question 1:
Should E be spelled op==?

My take is no.

→ Open question 2:
Should E exist for all types, as a "concept name" for "op== with 
parameters", or should we leave that, as it is now, on type-by-type 
basis to each class author individually?

My take is that yes, it should exist as a "concept name". I added 
qStringsEqual() in 5.10 as public API when adding QStringView, but it 
was relegated to private API before the release. When class authors are 
given the option to accept parameters for equality comparison, they will 
find novel ways to use it (like Qt::Fuzzy).

== O as public API ==

In C++20, O would be spelled op<=>, but this is not possible in C++17, 
so O needs to be an actual named function, not an operator. The 
question, and it's a rhetorical one, then becomes: should O be public 
API, and the (rhetorical) answer is "yes, because C++17 users will want 
to be able to access O in C++17 projects, too (in C++20, they could call 
op<=>)". This includes Qt's own implementation of O's.

The gold standard for named operators in C++ is to have the 
implementation as a hidden friend (think swap()), possibly a member 
function (lhs.swap(rhs)), a (namespaced) fallback version for built-in 
types (std::swap(int, int)) and a calling convention that takes this 
into account:

lhs.swap(rhs);
// or
std::swap(lhs, rhs); // if you know decltype(lhs), ...

using std::swap;
swap(lhs, rhs);
// or
std::ranges::swap(lhs, rhs); // ... if you don't.

Applied to our situation, this means:

- each (orderable) class supplies O as a hidden friend
- may supply it as a (non-static) member function
- Qt provides an implementation for built-in types in a namespace (we 
only have 'Qt').

And we have the calling convention:

lhs.O(rhs);
Qt::O(lhs, rhs); // if you know the type

using Qt::O;
O(lhs, rhs);
O_as_CPO(lhs, rhs); // if you don't

O_as_CPO is to O what std::ranges::swap is to swapping: an ADL-enabled 
version that allows to skip the using Qt::O/using std::swap;

The same would be applicable to E if Open Question 1 ends up resolved as 
no and Open Question 2 as "yes, named concept".

This leads to the finding that O (and, depending on the outcome of the 
first 

[Development] Heads-up: what's new in '6.6' doc needs your updates

2023-06-14 Thread Jani Heikkinen via Development
Hi all,
https://code.qt.io/cgit/qt/qtdoc.git/tree/doc/src/whatsnew/whatsnew66.qdoc 
seems to be quite unfinished ☹ It would be good to get some more content there 
for Qt 6.6 Beta1 (which is planned to happen during this week). So please do at 
least most important updates now; finalizing and polishing can then happen 
later in beta phase.

br,
Jani

-- 
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development


[Development] C++20 ctor-level [[nodiscard]] (was: Re: C++20 @ Qt)

2023-06-14 Thread Marc Mutz via Development
Hi,

TL;DR:
- we should add Q_DISCARD_CTOR to all ctors of classes with class-level 
[[nodiscard]], esp. RAII objects
- class-level [[nodiscard]] seems to be abused atm to get ctor-level 
[[nodiscard]] semantics in C++!7
- do we want to mark (almost) all ctors as [[nodiscard]], in the future?

Long form:

Ivan implemented Q_NODISCARD_CTOR for 6.6 and I've started to roll use 
of it our over the QtBase. I have some questions.

First, some terms:

I call this a class-level [[nodicard]]:

class [[nodiscard]] QSignalBlocker {  };

This is a C++17 feature and we can (and do) use it unconditionally.

I call this a ctor-level [[nodiscard]]:

class QSignalBlocker {
public:
[[nodiscard]]
explicit QSignalBlocker(QObject *o) ;

};

This is a C++20 feature (technically, it's a clarification, potentially 
retroactively applied to C++!7, but that still means we can't rely on 
it), which is why we need a macro.

I won't lecture about what the two so, you can read up on that on 
cppreference.com:
- https://en.cppreference.com/w/cpp/language/attributes/nodiscard
- Paper: wg21.link/p1771

The obvious first scenario is to cause warnings for code such as

// Listing 1
QMutexLocker();

Or any other RAII class. Clang warns about this with a class-level 
[[nodiscard]]. GCC does not.

So the first rule should be to make all ctors [[nodiscard]] in classes 
that are class-level [[nodiscard]].

So far so good.

Enter QScopedPointer. It currently has a class-level [[nodiscard]], but 
does that actually make sense? C++!7 allows us to return QScopedPointer 
from a function (guaranteed copy elision), so users can write functions 
that return QScopedPointer. But it's not like use of that return value 
is required to be required.

So I've come to think that this use of class-level [[nodiscard]] is wrong.

It's the only way to get the desired warning for Listing 1 in C++17 
(absent P1771), on some compilers, but the semantics are wrong. Even for 
pure RAII types: I might return a QMutexLocker from a function, but that 
shouldn't mean that function's return value is [[nodiscard]]. It might 
be perfectly ok to discard it. We don't know, yet we enforce that policy 
for everyone by making QMutexLocker class-level [[nodiscard]].

In fact, what we actually want is ctor-level [[nodiscard]] in these 
cases. For the time being, given we support C++17, I'm ok with having 
class-level [[nodiscard]] on pure RAII classes (ie. not on smart 
pointers), but come C++20, we should remove these in favour of 
ctor-level [[nodiscard]]s.

Here comes the can of worms: If you accept that Listing 1 is worth 
warning about, what about

 // Listing 2
 QSize(12, 32);

 // Listing 3
 QString("Hello, useless");

 // Listing 4
 QSlider(0, 100, this);

Does this mean that, like for most const member functions that have 
since gotten [[nodiscard]] because their only side-effect is to produce 
a return value, we should mark _all_ ctors [[nodiscard]], because their 
only side-effect is to produce an object?

Thanks,
Marc

-- 
Marc Mutz 
Principal Software Engineer

The Qt Company
Erich-Thilo-Str. 10 12489
Berlin, Germany
www.qt.io

Geschäftsführer: Mika Pälsi, Juha Varelius, Jouni Lintunen
Sitz der Gesellschaft: Berlin,
Registergericht: Amtsgericht Charlottenburg,
HRB 144331 B

-- 
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development