Re: [Development] How qAsConst and qExchange lead to qNN

2022-12-01 Thread A . Pönitz
On Wed, Nov 16, 2022 at 09:50:35AM -0800, Thiago Macieira wrote:
> On Tuesday, 15 November 2022 23:50:38 PST Marc Mutz via Development wrote:
> > > in a thread-safe manner (such that if something in
> > > the same thread or another thread-safely modifies that map, the original
> > > user isn't affected).
> > 
> > The above isn't thread-safe, it isn't even re-entrant, in the same way
> > that iteration using iterators isn't. This is a known issue whenever you
> > hand out references, and it's nothing that violates our
> > const-is-thread-safe promise, 
> 
> No, but it moves the responsibility for avoiding this problem to the user.
> 
> Right now, you can do:
>   for (auto elem : object.keyList()) {
>   operate(); // may recurse back into object and modify it
>   }
> 
> If you use a generator paradigm to return this key list, then the *user* must 
> know that they must create a local container with the items to be generated 
> and iterate over that. Performance-wise, this no different than if the Qt 
> code 
> created the container and returned it, but it has two drawbacks:
> 
> 1) the responsibility for knowing this
> 
> 2) if the Qt object already has a QList with this, then using a generator 
> paradigm enforces the need of a deep copy, when implicit would have been 
> cheaper

Exactly.

The only *safe* *and* *conveniently uniform* (a.k.a. "worth to have on a
Qt API level") use of a generator as return value (e.g. something that's
easily put into a delayed call) is to effectively store a full copy of
the later generated elements /somewhere/ or otherwise make sure that the
base object's lifetime or at least a subset of it that is capable to
create the elements is extended until the generator object dies (e.g.
by having some kind of ref count and COW on itself).

Currently this "storing of a full copy" is bumping a reference count
on an already existing Qt container, or creating one from scratch,
including a deep copy. In the envisioned new world, this would always
be an immediate full copy.

> > > Because you pointed to QStringTokenizer and that implicitly-
> > > copies a QString.
> > 
> > That's imprecise. QStringTokenizer extends rvalue lifetimes ("rvalue
> > pinning") so's to make this safe:
> > 
> > for (auto part : qTokenize(label->text(), u';'))
> 
> BTW, http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2012r2.pdf is 
> accepted for C++23 and moves the end of the temporaries' lifetimes to the end 
> of the full for statement.
> 
> Though we still need to work with C++17 and 20 for a while.
> 
> Also, sometimes I wonder if all the work you and I do to optimise these 
> things 
> matter, in the end. We may save 0.5% of the CPU time, only for that to be 
> dwarfed by whatever QtGui, QtQml are doing.

The Qt Project has easy access to a not completely trivially-sized test
bed for API experiments which has stability promises one magnitude less
and potential damage done to downstream dependencies several magnitudes
less than similar activities in QtBase: Qt Creator. Interested parties
could just check out the sources, and start running experiments without
being bound to long-term API contract and with simple reverts at hand
when experiments fail.

I can even give a hint where to start: A rather heavily used string-ish
class is Utils::FilePath, and there are at least half a dozen "obvious"
candidates for an NOI: E.g. .suffix() currently returns a QString that
is /always/ a copy of a part of the underlying storage, so according to
my understanding this "obviously" would "have to be" a QStringView in a
"new world" API.

I'd be happy to see the results of the experiment from someone who
thinks this kind of API is a good idea in the form of profiler results
and consequences for uses of this API in "user" code. [I know mine.]

Andre'

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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-12-01 Thread A . Pönitz
On Tue, Nov 15, 2022 at 08:07:50AM +, Marc Mutz via Development wrote:
> On 14.11.22 23:04, A. Pönitz wrote:
> >> Marc’s proposal of a Non-Owning Interface is already
> >> become manifest in QRegion::begin/end
> >>
> >> https://doc.qt.io/qt-6/qregion.html#begin
> >>
> >> allowing us to write
> >>
> >>
> >> for (auto rect : region) doSomethingWith(rect);
> > Yes, and that's fine [but not quite matching the rest of the discussion
> > of using spans?]
> > 
> >> (while QRegion::rects will have to create a QList even if there is
> >> only a single rect in the inline storage, which is then not a QList).
> >>
> >> This is a*good*  addition to Qt. I think we can make more such
> >> additions to Qt, in places where it makes a real difference for
> >> today’s client code using owning containers, and without changing the
> >> world.
> > Fine with me.
> > 
> > With emphasis on "addition" and "real", and notable absense of "change"
> > and "removal"...
> 
> QVector QRegion::rects() const _was_ removed for Qt 6.0.

I was referring to the introduction of begin() and end().

The removal of QVector QRegion::rects() const was not needed,
but did not particular hurt.
 
> The trick to treat QRegion as a container of QRect, simplifying both 
> users and implementation of QRegion (no more vectorize()), only worked 
> because QRegion is home to only one collection. It's also a trick you 
> need to see to be able to use it,

I don't think so.

> so discoverability is poor. In the 
> general case, a given class may have more than one collection of items.
> E.g. if circular windows became the new rage, QRegion could be a 
> collection not just of rects, but also of ellipses. Then what?
> 
> Enter span-retuning getters:
> 
> for (QRect rect : region.rects())
>   ~~~
> for (QEllipse ell : recion.ellipses())
>   ~~~

Or return per-type proxy-objects implementing begin() and end().

> [..]
> There's a logical incompatibility between "QRegion as a QRect container 
> = good" and "QRection::rects() returning a span of QRect = bad".
> 
> Either both are good ideas or neither are.

No. The idea is that QRegion can be anything it want to /internally/,
but there's a way to consider is as a container of QRects.

> They both grant the same freedoms and put the same constraints on the
> implementation of QRegion, but one is more flexible than the other.

Returning a span requires a container somewhere /and/ adds the implicit
requirement to the lifetime of this storage. It is putting /more/
constraints on the implementation of QRegion.

The container-returning one is more flexible as this can be either
a container that's part of the structure, or be created on-the-fly.

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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-17 Thread Thiago Macieira
On Thursday, 17 November 2022 10:32:50 PST Elvis Stansvik wrote:
> Fermat's Last QString Vectorization Update :p

Everything is already set to Gerrit. What I haven't done is benchmark it to 
confirm the theoretical runs in LLVM-MCA.

It starts at
https://codereview.qt-project.org/c/qt/qtbase/+/386952

See the search at
https://codereview.qt-project.org/q/
is:open+owner:thiago.macieira%2540intel.com+message:QString

The changes are mostly organised as "reorganise the pre-AVX code", then 
"rewrite AVX2 code" then "add AVX512VL code" for each of the functions.

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



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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-17 Thread Thiago Macieira
On Thursday, 17 November 2022 10:24:35 PST Volker Hilsheimer via Development 
wrote:
> > Though I am postponing the QString vectorisation update to 6.6 because I
> > don't have time to collect the benchmarks to prove I'm right before
> > feature freeze next Friday.
> 
> Next Friday is the platform & module freeze. Feature freeze is not until
> December 9th, i.e. another 3 weeks to go.

Next Friday is also the day after Thanksgiving here in the US.

I don't expect I can finish the benchmarking in 3 weeks, not considering I need 
to finish the IPC work and that includes starting a couple of changes that I 
haven't started yet (like the ability to clean up after itself).

For the benchmarking, I've already collected the data by instrumenting each of 
the functions in question and running a Qt build, a Qt Creator start and a Qt 
build inside Qt Creator:

qt-build-data.tar.xz: 1197.3 MB
qtcreator-nosession.tar.xz: 2690.0 MB
qtcreator-session.tar.xz: 35134.6 MB

The data retains its intra-cacheline alignment.

The way I'm seeing it, is that for each of the algorithm generations, I need 
to:
1) find the asymptotic limits, given L1, L2 and L3 cache sizes
That is, the algorithms should be fast enough that the bottleneck is the 
transfer of data. There's no way that running qustrchr on 35 GB is going to be 
bound by anything other than RAM bandwidth or, in my laptop's case, the NVMe. 
So what are those limits?

2) benchmark at several data set sizes (half to 75% of L1, half to 75% of L2) 
on several generations
Confirm that the algorithm is running close to or better than the ideal run 
that LLVM-MCA showed when I designed them. I know I can benchmark throughput 
to see if we're reaching the target bytes/cycle processing, but I don't know 
if I can benchmark the latency. I also don't know if it matters.

3) benchmark at several input sizes (i.e., strings of 4 characters, 8 
characters, etc.)
Same as #2, but instead of running over the sample that adds up to a certain 
data size, select the input such that the strings have always the same size.

4) compare to the previous generation's algorithm to confirm it's actually 
better
Different instructions have different pros and cons; what might work for one at 
a given data size may not for another

The algorithms available are:
* baseline SSE2: no comparisons
* SSE 4.1: compare to baseline SSE2, current SSE 4.1
* AVX2: compare to new SSE 4.1, current AVX2
* AVX512 with 256-bit vectors ("Avx256"): compare to new AVX2

I plan on collecting data on 3 laptop processors (Haswell, Skylake and Tiger 
Lake) and 2 desktop processors (Coffee Lake and Skylake Extreme). The Skylake 
should match the performance of almost all the Skylake and derivatives since 
2016; the Coffee Lake NUC has the same processor as my Mac Mini; the Tiger Lake 
should be the performance of modern processors. The Skylake Extreme and the 
Tiger Lake can run the AVX512 code too. I don't know if the AVX512 code on 
Skylake will show a performance gain or a loss, because despite using only 256 
bits, it may need to power on the OpMask registers. If it doesn't, I will 
adjust the feature detection to only apply to Ice Lakes and later.

I have a new Alder Lake which would be nice to benchmark, to get the 
performance on both the Golden Cove P-core and the Gracemont E-core, but the 
thing runs Windows and the IT-mandated virus scans, so I will not bother.

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



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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-17 Thread Elvis Stansvik
Den tors 17 nov. 2022 kl 18:46 skrev Thiago Macieira
:
>
> On Thursday, 17 November 2022 02:04:54 PST Marc Mutz via Development wrote:
> > > Also, sometimes I wonder if all the work you and I do to optimise these
> > > things matter, in the end. We may save 0.5% of the CPU time, only for
> > > that to be dwarfed by whatever QtGui, QtQml are doing.
> >
> > I hear you, but I'm not ready to give in just yet.
>
> Nor am I.
>
> Though I am postponing the QString vectorisation update to 6.6 because I don't
> have time to collect the benchmarks to prove I'm right before feature freeze
> next Friday.

Fermat's Last QString Vectorization Update :p

Elvis

>
> --
> Thiago Macieira - thiago.macieira (AT) intel.com
>   Cloud Software Architect - Intel DCAI Cloud Engineering
>
>
>
> ___
> Development mailing list
> Development@qt-project.org
> https://lists.qt-project.org/listinfo/development
___
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-17 Thread Volker Hilsheimer via Development



> On 17 Nov 2022, at 18:45, Thiago Macieira  wrote:
> 
> On Thursday, 17 November 2022 02:04:54 PST Marc Mutz via Development wrote:
>>> Also, sometimes I wonder if all the work you and I do to optimise these
>>> things matter, in the end. We may save 0.5% of the CPU time, only for
>>> that to be dwarfed by whatever QtGui, QtQml are doing.
>> 
>> I hear you, but I'm not ready to give in just yet.
> 
> Nor am I.
> 
> Though I am postponing the QString vectorisation update to 6.6 because I 
> don't 
> have time to collect the benchmarks to prove I'm right before feature freeze 
> next Friday.


Next Friday is the platform & module freeze. Feature freeze is not until 
December 9th, i.e. another 3 weeks to go.

Volker

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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-17 Thread Thiago Macieira
On Thursday, 17 November 2022 02:04:54 PST Marc Mutz via Development wrote:
> > Also, sometimes I wonder if all the work you and I do to optimise these
> > things matter, in the end. We may save 0.5% of the CPU time, only for
> > that to be dwarfed by whatever QtGui, QtQml are doing.
> 
> I hear you, but I'm not ready to give in just yet.

Nor am I.

Though I am postponing the QString vectorisation update to 6.6 because I don't 
have time to collect the benchmarks to prove I'm right before feature freeze 
next Friday.

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



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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-17 Thread Marc Mutz via Development
Hi Thiago,

On 16.11.22 18:50, Thiago Macieira wrote:
> On Tuesday, 15 November 2022 23:50:38 PST Marc Mutz via Development wrote:
>>> in a thread-safe manner (such that if something in
>>> the same thread or another thread-safely modifies that map, the original
>>> user isn't affected).
>>
>> The above isn't thread-safe, it isn't even re-entrant, in the same way
>> that iteration using iterators isn't. This is a known issue whenever you
>> hand out references, and it's nothing that violates our
>> const-is-thread-safe promise,
> 
> No, but it moves the responsibility for avoiding this problem to the user.
> 
> Right now, you can do:
>for (auto elem : object.keyList()) {
>operate(); // may recurse back into object and modify it
>}
> 
> If you use a generator paradigm to return this key list, then the *user* must
> know that they must create a local container with the items to be generated
> and iterate over that. Performance-wise, this no different than if the Qt code
> created the container and returned it, but it has two drawbacks:
> 
> 1) the responsibility for knowing this

Not necessarily. E.g. when the co-routine implementation uses the equivalent of 
an indexed 
loop, it immunizes itself from changes to the container while it's 
suspended. It can also post a re-entrancy guard in the class' data, like 
we sometimes already do in event handlers and often do in slots, to at least 
detect and mitigate the issue.

This isn't different from emitting signals or calling virtual functions 
while iterating, and the solutions are the same, and, largely, if not 
completely, under the control of the co-routine implementation.

That said, it's not entirely clear to me how widespread such issues are. After 
all, the user or a generator sees the potentially-re-entering code, it's in the 
function he's presently writing/analyzing, and not hidden in the way 
signal/slot connections or even virtual functions hide the issue by having 
far-removed code cause the problem. So I don't know whether the benefits of 
lazy evaluation outweight or are dwarfed by this issue.

> 2) if the Qt object already has a QList with this, then using a generator
> paradigm enforces the need of a deep copy, when implicit would have been
> cheaper

I hasten to interject here that the code you wrote above actually does 
deep-copy in that case (hidden detach in the for loop).

Apart from that, we're circling back to the assumption that a class would hold 
or return a QList for the sake of QList. For holding, and also for returning, 
if one must return an owning container, a QVLA or otherwise SBO'd container 
would be more appropriate in many cases. The lack of such containers in Qt 
begets the use of QList in the first place. To get out of this tread-mill, one 
needs to look outside the Qt echo chamber, to std C++ (std::u16string, 
std::pmr), Folly (F14 (hash table), fbstring (SSO, CoW only for large 
strings)), Python (strings are QAnyString with SSO there), LLVM 
(llvm::SmallVector, StringRef, ArrayRef), Mozilla's JS strings (L1/UTF-16 
QAnyString, SSO). Then work backwards from these kinds of containers to how we 
can enable them in Qt.

>>> Because you pointed to QStringTokenizer and that implicitly-
>>> copies a QString.
>>
>> That's imprecise. QStringTokenizer extends rvalue lifetimes ("rvalue
>> pinning") so's to make this safe:
>>
>>  for (auto part : qTokenize(label->text(), u';'))
> 
> BTW, http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2012r2.pdf is
> accepted for C++23 and moves the end of the temporaries' lifetimes to the end
> of the full for statement.

Hallelujah! Thanks, Nico!

> Though we still need to work with C++17 and 20 for a while.
> 
> Also, sometimes I wonder if all the work you and I do to optimise these things
> matter, in the end. We may save 0.5% of the CPU time, only for that to be
> dwarfed by whatever QtGui, QtQml are doing.

I hear you, but I'm not ready to give in just yet.

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] How qAsConst and qExchange lead to qNN

2022-11-16 Thread Marc Mutz via Development
Hi Volker,

On 14.11.22 19:00, Volker Hilsheimer wrote:
[...]
> Today, the vast majority of client code already has an owning container that 
> gets passed around by copy or as const references.

There are important subsets that don't, e.g. the aforementioned 
serialisation APIs and where 10%+ executable code savings can be had:

- https://codereview.qt-project.org/c/qt/qtbase/+/353688
- https://codereview.qt-project.org/c/qt/qt3d/+/263444
- https://codereview.qt-project.org/c/qt/qt3d/+/157737

> In Qt, we usually store data in the implementation in QList and QString. A 
> test case that artificially creates a ton of QString instances perhaps rather 
> falls into the small and isolated use case that doesn’t translate very well 
> into real applications.

I disagree that tst_qsettings isn't a real-world application. Most Qt 
applications will have a part that does serialisation into one of JSON, 
QSettings, or XML, and checking the impact of QString -> QAnyStringView 
on a test that does makes perfect sense to me to gauge the impact on 
that part of the API. Granted, it's a maximum-attainable savings gauge. 
But it's one of the better indications we have, apart from looking at 
the assembly generated, on how QAnyStringView affects codegen.

That said, there are also Qt apps that don't use tr() and just pass 
string literals for human-readable text. Those would get a similar 
reduction in executable code size if _all_ QString setters were replaced 
by QAnyStringView ones.

The file APIs also suffer from the forced use of UTF-16 for paths (cf. 
https://bugreports.qt.io/browse/QTBUG-104095). Hands up: who has in the 
past been lazy and said "meh, QFile::encodeName(), I'll just skip that" 

> There are most certainly classes where it’d be good if we could replace that 
> implementation with e.g. a std::pmr::vector with an optimised allocator. And 
> then being stuck with a QList API forces both us and client code to construct 
> suboptimal data structures. And there are APIs where replacing the QString 
> version with QAnyStringView makes perfect sense (such as all remaining 
> fromString factory functions).
> 
> But that we either replace all, or none of our APIs with something taking a 
> view or a span are perhaps not the only outcomes of this conversation.

https://bugreports.qt.io/browse/QTBUG-101388 explicitly proposes to 
start where there's the most bang for the buck, and I've been following 
it to the letter.

> Can we focus on the cases with the biggest wins, like you already did with 
> QRegion, QSettings, and QObject::setObjectName? What APIs in Qt that take a 
> QString are usually called with a string literal in real applications, rather 
> than with an already created QString object (that is in turn the result of 
> user input or reading from some storage)?

Exactly what https://bugreports.qt.io/browse/QTBUG-101388 proposes.

> What was the outcome of the QObject::setObjectName change for e.g. Qt Creator?

I don't have numbers for QtC, but I have numbers for uic-generated code:

https://codereview.qt-project.org/c/qt/qtbase/+/303859/comments/a3b3dfa2_c7b1ee67

That's not separated from the rest of the code, so I take it fits the 
real-world benchmark better: In particular those 1.7% are _just_ 
setObjectName(). Just in uic-generated code. 1.7% of executable code 
previously wasted on temporary QString creation in a real-life library. 
Gone.

And the simplest code is also the most efficient:

   setObjectName("m_foobar");

The sad fact is that this is just rolling back the inefficiencies caused 
by making objectName a QString in Qt 4.

> What would a QSpan-returning implementation of e.g. QObject::findChildren or 
> QItemSelectionModel::selectedIndexes look like? Is that even feasible without 
> using coroutines?

It's not possible. As per my previous emails, a span can only be used to 
return stored contiguous data. Returning computed or non-contiguous data 
requires a co-routine (or callbacks) (NOIv1 vs NOIv2).

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] How qAsConst and qExchange lead to qNN

2022-11-16 Thread Thiago Macieira
On Tuesday, 15 November 2022 23:28:46 PST Marc Mutz via Development wrote:
> In the problematic case where a temporary container is created at the
> call site for the sole purpose of providing function arguments, it's
> specifically the dec, though, which is problematic (deref-to-zero has an
> acquire fence). The compiler cannot prove that the atomic hasn't been
> manipulated, so it can't optimize the deref out and go directly to
> deallocation. This includes the case of a defaulted extra argument
> (https://bugreports.qt.io/browse/QTBUG-98117), maybe not with QString,
> anymore, but, most recently, with QKeySequence (which prompted the
> addActions() revamp).

Indeed. I'd really, REALLY love to be able to inform the compiler about the 
contract of reference counters and of "really const" parameter passing. I 
don't know if C++ contracts would help here.

> Seeing as destruction of temps is sequenced before the end of the
> full-expression and seeing as atomics are synchronization points, the
> C++ memory model says that these atomic decs _will_ hold up execution of
> the next statements.

Partially. The CPU is slightly smarter than that and can continue to 
speculatively run past the atomic operation, so long as the behaviour is as-is 
to not having done so.

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



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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-16 Thread Thiago Macieira
On Tuesday, 15 November 2022 23:50:38 PST Marc Mutz via Development wrote:
> > in a thread-safe manner (such that if something in
> > the same thread or another thread-safely modifies that map, the original
> > user isn't affected).
> 
> The above isn't thread-safe, it isn't even re-entrant, in the same way
> that iteration using iterators isn't. This is a known issue whenever you
> hand out references, and it's nothing that violates our
> const-is-thread-safe promise, 

No, but it moves the responsibility for avoiding this problem to the user.

Right now, you can do:
  for (auto elem : object.keyList()) {
  operate(); // may recurse back into object and modify it
  }

If you use a generator paradigm to return this key list, then the *user* must 
know that they must create a local container with the items to be generated 
and iterate over that. Performance-wise, this no different than if the Qt code 
created the container and returned it, but it has two drawbacks:

1) the responsibility for knowing this

2) if the Qt object already has a QList with this, then using a generator 
paradigm enforces the need of a deep copy, when implicit would have been 
cheaper

> > Because you pointed to QStringTokenizer and that implicitly-
> > copies a QString.
> 
> That's imprecise. QStringTokenizer extends rvalue lifetimes ("rvalue
> pinning") so's to make this safe:
> 
> for (auto part : qTokenize(label->text(), u';'))

BTW, http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2012r2.pdf is 
accepted for C++23 and moves the end of the temporaries' lifetimes to the end 
of the full for statement.

Though we still need to work with C++17 and 20 for a while.

Also, sometimes I wonder if all the work you and I do to optimise these things 
matter, in the end. We may save 0.5% of the CPU time, only for that to be 
dwarfed by whatever QtGui, QtQml are doing.

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



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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-16 Thread Volker Hilsheimer via Development
> On 16 Nov 2022, at 09:54, Marc Mutz via Development 
>  wrote:

[…]

>>> But UTF-16 is sacrosanct in Qt. It's a cult. Irregardless of how many
>>> deep copies it takes to convert to and from UTF-16 from native
>>> encodings, people still worship it as god-given. It's not.


[…]

> If CoW is so superior, why did we kick out the industry-standard 
> unsharable state? Because otherwise the performance sucked. Well, CoW 
> performance sucks. Esp. for small strings, which are the majority of 
> strings. Folly uses CoW only for strings > 256 chars, IIRC. But CoW in 
> Qt is another cult, so we rather break correctness of our APIs than back 
> off the cult.
> 
> It saddens me to see the project so stuck in the 90s.



Hi Marc,


I respect and admire your passion for this topic, and your desire to make Qt 
better on this front. From watching several of your Meeting C++ talks, I know 
that you strongly believe that this - owning containers, CoW - is a 
make-or-break topic for the future of Qt. And you put energy into this 
discussion because you want Qt to succeed also in a C++20 world, where people 
have perhaps adapted, and expect to be able to use, a programming style where 
perhaps everything is a stream or asynchronous and awaitable.

But your recent use of divisive language does nothing to further your cause. 
Many of us are evidently not convinced that this is critical for the success of 
Qt, because what we experience every day when using Qt, or when looking at user 
feedback, is that other things are more important. And with that perspective, 
it’s hard to see how the cost of moving to non-owning interfaces is justified 
by the benefits.

The cost comes in many forms, and is very tangible: making the change, dealing 
with the code churn, adapting to the new style, living with inconsistency 
across a large 3rd party eco system of libraries; and last but not least cost 
of opportunity and delay for other things we could spend our time on instead. 
On top of all that, some of our historical attempts in the project have made 
people weary of changes in the Core.

And as a whole, we have not bought into the benefits of NOI for a real software 
system, or for the Qt API as a whole. Again, historical changes that were made 
with the promise of improvements have not always delivered, and often resulted 
in having to deal with a lot of code changes just to end up with something that 
performs worse. And if a costly change to Qt addresses none of the problems 
that users bring to us every day, then we should probably not spend time on it. 
We try to get more people over to Qt 6, in a very compelling competitive 
landscape with new languages, game engines, and cross-platform UI technology. 
The last thing we need right now is more deprecations and changes that people 
have to constantly chase after. So, if people don't want to change things, then 
it’s not because they are addicted to a cult or idiom, and I expect that you 
try to respect their perspective as well.

Yes, this comes with the risk that we are focusing too much on the short term. 
But even when looking further ahead, large scale changes to a complex systems 
are almost never a good idea. Qt is a complex system, and we can’t predict the 
impact of large scale changes. But do we have to?

I don’t see anyone claiming that Qt is flawless, that our current tools 
(including CoW) are perfect for all situations, and that we can’t or shouldn't 
make improvements in those places where the status quo is bad enough. Most 
people participating in this conversation agree that we should make respective 
additions to Qt, where appropriate. Can we start with that? You proposed that 
we add a QSpan that is only used internally until at least Qt 6.6; I assume 
that you had some internal use cases in mind where we can see how this would be 
used, discuss a real implementation, evaluate the (change of) complexity for 
users and for implementors, and see what we learn from that.

I asked earlier: what has been the impact of the QObject::setObjectName change 
for a real application (one that uses .ui files will have a lot of those 
calls)? It’s one of the (few) APIs in Qt that is always called with a string 
literal. Did anyone notice anything breaking, or did it cause any porting 
effort? It seems like a very sensible change to me (which doesn’t mean that I 
believe that e.g. QLineEdit::setText needs be changed as a logical next step). 
But perhaps it’s also evidence that it’s a good idea to make that kind of 
change in selected places (like fromString factory functions).

Volker

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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-16 Thread Marc Mutz via Development
Hi Philippe,

On 15.11.22 12:50, Philippe wrote:
> On Tue, 15 Nov 2022 08:52:24 +
> Marc Mutz via Development  wrote:
>> QAnyStringView, QUtf8StringView and, later, QAnyString, QUtf8String,
>> can be used to to make UTF-8 a first-class citizen in Qt.
> 
> Maybe, but I see a deviation from simplicity (not "Qt-ish")

Do you? Then answer me this: what is QByteArray? It's not an array of 
bytes, because it has toLower() and toUpper(), which don't make sense 
for binary data. It also isn't a string, because, while it's contents 
are to be treated as UTF-8, QByteArray(u8"ä").toUower() != u8"Ä".

Now, tell, me: What's the Qt was to convert a UTF-8 string to lower-case?

Answer: fromUtf8().toLower().toUtf8()

How is any of this simple? An API isn't automatically simple just because it 
minimizes the number of classes. A simple API is one where each class 
has one responsibility, and one responsibility only. Don't confuse 
familiarity with simplicity. A new user will have no problem with an API 
where QByteArray is _only_ an octet-stream and QUtf8String is _only_ a 
UTF-8 string.

And don't even get me started on QByteArray::fromRawData().

The simplest possible string API is one where owning and non-owning 
containers are separate types, and we have different types for each 
major encoding, incl. binary:

Encoding | value_type | Owning| Non-owning | Rem.  |
-++---++---|
Latin-1  | char?  | QLatin1String | QLatin11StringView | Qt 7  |
UTF-8| char8_t| QUtf8String   | QUtf8StringView| C++20 |
UTF-16   | char16_t   | QString   | QStringView|   |
-++---++---+
any of ^ | ---| QAnyString| QAnyStringView |   |
-++---++---+
binary   | std::byte  | QByteArray| QByteArrayView |   |
-++---++---+

L1 is really US-ASCII, but it makes sense to not throw away the 8th bit, 
and go directly to L1. And no, UTF-8 is not a full replacement for 
L1/US-ASCII, because it is a variable-length encoding (size() check for 
op==, also think how to implement QString::insert(UTF-8) vs. 
QString::insert(L1) efficiently to see why).

We have space for one more entry in QAnyStringView::Tag, so we could 
support UTF-32 or Local8Bit, too, going forward.

This is expressive, and simple API. No more head-scratching over whether 
a QByteArray-taking function expects UTF-8 or binary data. No more bugs 
because owning containers are sometimes not NUL-terminated, even though 
they promise to always be. No more segfaults because QString backing 
data was statically allocated and the library was already unloaded (can 
not only happen on plugin unload, but also during shutdown, or so I'm told).

You may belittle these problems as being irrelevant in practice, but 
it's a kind of problem that, if it strikes, leaves you dumbfounded. As 
opposed to statically-detectable lifetime issues with non-owning 
containers. I don't know about you, but I prefer *facepalm* problems 
readily diagnosed in the IDE over multi-night debugging sessions.

>> But UTF-16 is sacrosanct in Qt. It's a cult. Irregardless of how many
>> deep copies it takes to convert to and from UTF-16 from native
>> encodings, people still worship it as god-given. It's not.
> 
> If this is "sacrosanct", this is simply because many people appreciate
> QString for its rich API and ease of use. This has to be respected.
Note how I'm not saying to remove QString in favour of u16string. But we 
have a way to abstract said rich API from the underlying storage now 
(via QStringView). The idea is to provide rich API and ease of use for 
UTF-8 strings, too.

 >
 >> There's nothing inherently Qt-ish about owning containers.
 >
 > Yes and no, because owning containers are part of the very "Qt-ish"
 > Implicit Sharing idiom, which one is _great_ for the ease of use, safety
 > and optimization it provides.
 >
 > ~115 Qt classes: https://doc.qt.io/qt-6/implicit-sharing.html
Regardless of how you think about implicit sharing, you must agree that 
Qt has perverted that:

We kicked out the unsharable state, normally entered when handing out 
mutable references:

QString c = "mouse";
auto it = c.begin();
auto copy = c;
*it = 'h';
assert(copy == "mouse"); // nope: it's house, now

If CoW is so superior, why did we kick out the industry-standard 
unsharable state? Because otherwise the performance sucked. Well, CoW 
performance sucks. Esp. for small strings, which are the majority of 
strings. Folly uses CoW only for strings > 256 chars, IIRC. But CoW in 
Qt is another cult, so we rather break correctness of our APIs than back 
off the cult.

It saddens me to see the project so stuck in the 90s.

Thanks,
Marc

-- 
Marc Mutz 
Principal Software Engineer

The Qt Company
Erich-Thilo-Str. 10 12489

Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-16 Thread Ulf Hermann via Development

The above isn't thread-safe, it isn't even re-entrant, in the same way
that iteration using iterators isn't. This is a known issue whenever you
hand out references, and it's nothing that violates our
const-is-thread-safe promise, otherwise


... which is why we prefer to hand out (implicitly shared) copies rather 
than references.




  static const QMap map = ~~~;
  // T1
  map["x"].size();


Our container classes do hand out references, because containers have 
to. But in other places we avoid it.


best regards,
Ulf
___
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-16 Thread Ulf Hermann via Development

That's a deep copy. A deep copy of a string is obviously more expensive
than the overhead of calling the QString ctor and dtor.


That remains to be proven. A rule of thumb for atomics is that they're
two orders of magnitude slower than a normal int. They also still act as
optimizer firewalls. With that rule of thumb, copying 50 char16_t's is
faster than one ref-count update. What really is the deciding point is
whether or not there's a memory allocation involved. I mentioned that
for many use-cases, therefore, a non-CoW SBO container is preferable over a CoW
non-SBO one.


As stated elsewhere, the real problem about the deep copy is the 
allocation involved in it, not necessarily the copying of the elements.



I can't say what case is more common. What I can say is that the risk of
creating deep copies sounds worse to me than the risk of calling the
QString ctor and dtor too often.

This is what I mean with "fuzzy". We don't really have the data to
support a move to QAnyStringView for all of our API.


I can say with firm belief that, _atm_, passing QString is more common.
But this is a self-fulfilling fact. The tst_qsettings experiment shows
what can happen if you port an API that doesn't naturally receive
pre-made QStrings.


Are we in a position to change how people use our APIs? I can say with 
firm belief that most existing applications using Qt will not be 
rewritten from the ground up to avoid passing QString and QList to Qt 
APIs. So they will pay the cost of deep copies if we force 
QAnyStringView and QSpan on them by changing our API. There are lots of 
existing Qt applications. Your CO2 calculation doesn't look that great then.


New APIs that are not used in the "wrong" way all over the world, yet, 
are a different story. If we can provide a clean QAnyStringView or QSpan 
override without ambiguities, it's also  a different story. (But adding 
complexity to the API is still a thing).


Maybe we can approach this in a module-by-module way. QtWidgets is 
probably pretty "bad" regarding QList and QString arguments, and no one 
will change their old widgets applications to suit our newfangled 
containers.


QtQml and QtQuick, on the other hand, hardly have any C++ API, and most 
QML and QtQuick applications are not that old. We might get away with 
changing API there.


QtCore and QtGui is where the most difficult decisions are to be taken, 
I guess.


best regards,
Ulf
___
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-15 Thread Marc Mutz via Development
HI Thiago,

On 15.11.22 17:33, Thiago Macieira wrote:
> On Tuesday, 15 November 2022 01:42:55 PST Marc Mutz via Development wrote:
>>> Returning as an iteratable interface requires that we return a proxy
>>> object, like QRegularExpressionMatch, so that the solution is
>>> thread-safe. This is neither simple to understand, to code, or to port
>>> existing code over to. It also requires copying the data over (hopefully,
>>> implicitly) to the proxy object, so it doesn't solve anything.
>>
>> I disagree on all points. QREM is complicated because we need to
>> shoehorn a coroutine into an iterator concept. Same with
>> QStringTokenizer. Coroutines with lazy sequences (generator<>) are very easy
>> to implement and use.
>>
>> If we're to discuss further, please watch my Meeting C++ presentation,
>> which lays out all the pros and cons I'm aware of. No need to re-iterate
>> them in text here. https://youtu.be/tvdwYwTyrig
> 
> It will take some time to watch it.

Thanks. 2× is about the right speed :)

> In the meantime, I'd appreciate a short answer on how you return the keys from
> a stored associative map,

   std::generator func() {
  for (auto &[key, value] : m_assoc_cont)
  co_yield key;
   }

> in a thread-safe manner (such that if something in
> the same thread or another thread-safely modifies that map, the original user
> isn't affected).

The above isn't thread-safe, it isn't even re-entrant, in the same way 
that iteration using iterators isn't. This is a known issue whenever you 
hand out references, and it's nothing that violates our 
const-is-thread-safe promise, otherwise

 static const QMap map = ~~~;
 // T1
 map["x"].size();

would also be affected. The promise doesn't end when you return from the 
const member function, it extends to references handed out in the 
process (op[], begin, end, any lazy sequence).

Of course, in special cases where you have the need for actual 
thread-safety, some form of owning container will be required. But then 
we're talking about maybe 0,1% of all APIs, if even that many.

> Because you pointed to QStringTokenizer and that implicitly-
> copies a QString.

That's imprecise. QStringTokenizer extends rvalue lifetimes ("rvalue 
pinning") so's to make this safe:

for (auto part : qTokenize(label->text(), u';'))

even though the return value of label->text() would ordinarily be 
destroyed at the end of the full-expression, which, seeing the way 
ranged-for is specified:

{
auto && __range = qTokenize(label->text(), u';');
// would be here
auto __begin = begin-expr(__range);
auto __end = end-expr(__range);
while (__begin != __end) {
decl = *__begin;
++__begin;
}
}

iow: too early. It does this only for rvalue owning containers, not for 
lvalues and not for views (!borrowed_range in C++20 ranges parlor). This 
has nothing to do with multi-threading.

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] How qAsConst and qExchange lead to qNN

2022-11-15 Thread Marc Mutz via Development
Hi Thiago,

On 15.11.22 17:25, Thiago Macieira wrote:
> On Tuesday, 15 November 2022 00:52:24 PST Marc Mutz via Development wrote:
>> That remains to be proven. A rule of thumb for atomics is that they're
>> two orders of magnitude slower than a normal int. They also still act as
>> optimizer firewalls. With that rule of thumb, copying 50 char16_t's is
>> faster than one ref-count update. What really is the deciding point is
>> whether or not there's a memory allocation involved. I mentioned that
>> for many use-cases, therefore, a non-CoW SBO container is preferable over a
>> CoW non-SBO one.
> 
> That's irrelevant so long as we don't have SBO containers.

We have not-quite-SBO-but-close QVLA for arbitrary types, and we have 
std::string as a stand-in for QByteArray and u16string for QString. 
There are also tons of such container classes in 3rd-party libraries, 
which suddenly become viable because we've decoupled the API from the 
implementation.

> So what we need to really compare are memory allocations versus the atomics. A
> locked operation on a cacheline on x86 will take in the order of 20 cycles of
> latency on top of any memory delays[1], but do note the CPU keeps running
> meanwhile (read: an atomic inc has a much smaller impact than an atomic dec
> that uses the result). A memory allocation for a single byte will have an
> impact bigger than this, hundreds of cycles.
> 
> Therefore, in the case of CoW versus deep copy, CoW always wins.
> 
> [1] https://uops.info/html-instr/INC_LOCK_M32.html says 23 cycles on an 11-
> year-old Sandy Bridge, 19 on Haswell, 18 on everything since Skylake.

In the problematic case where a temporary container is created at the 
call site for the sole purpose of providing function arguments, it's 
specifically the dec, though, which is problematic (deref-to-zero has an 
acquire fence). The compiler cannot prove that the atomic hasn't been 
manipulated, so it can't optimize the deref out and go directly to 
deallocation. This includes the case of a defaulted extra argument 
(https://bugreports.qt.io/browse/QTBUG-98117), maybe not with QString, 
anymore, but, most recently, with QKeySequence (which prompted the 
addActions() revamp).

Seeing as destruction of temps is sequenced before the end of the 
full-expression and seeing as atomics are synchronization points, the 
C++ memory model says that these atomic decs _will_ hold up execution of 
the next statements.

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] How qAsConst and qExchange lead to qNN

2022-11-15 Thread Thiago Macieira
On Tuesday, 15 November 2022 01:42:55 PST Marc Mutz via Development wrote:
> > Returning as an iteratable interface requires that we return a proxy
> > object, like QRegularExpressionMatch, so that the solution is
> > thread-safe. This is neither simple to understand, to code, or to port
> > existing code over to. It also requires copying the data over (hopefully,
> > implicitly) to the proxy object, so it doesn't solve anything.
> 
> I disagree on all points. QREM is complicated because we need to
> shoehorn a coroutine into an iterator concept. Same with
> QStringTokenizer. Coroutines with lazy sequences (generator<>) are very easy
> to implement and use.
>
> If we're to discuss further, please watch my Meeting C++ presentation,
> which lays out all the pros and cons I'm aware of. No need to re-iterate
> them in text here. https://youtu.be/tvdwYwTyrig

It will take some time to watch it.

In the meantime, I'd appreciate a short answer on how you return the keys from 
a stored associative map, in a thread-safe manner (such that if something in 
the same thread or another thread-safely modifies that map, the original user 
isn't affected). Because you pointed to QStringTokenizer and that implicitly-
copies a QString.

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



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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-15 Thread Thiago Macieira
On Tuesday, 15 November 2022 00:52:24 PST Marc Mutz via Development wrote:
> That remains to be proven. A rule of thumb for atomics is that they're
> two orders of magnitude slower than a normal int. They also still act as
> optimizer firewalls. With that rule of thumb, copying 50 char16_t's is
> faster than one ref-count update. What really is the deciding point is
> whether or not there's a memory allocation involved. I mentioned that
> for many use-cases, therefore, a non-CoW SBO container is preferable over a
> CoW non-SBO one.

That's irrelevant so long as we don't have SBO containers.

So what we need to really compare are memory allocations versus the atomics. A 
locked operation on a cacheline on x86 will take in the order of 20 cycles of 
latency on top of any memory delays[1], but do note the CPU keeps running 
meanwhile (read: an atomic inc has a much smaller impact than an atomic dec 
that uses the result). A memory allocation for a single byte will have an 
impact bigger than this, hundreds of cycles.

Therefore, in the case of CoW versus deep copy, CoW always wins.

[1] https://uops.info/html-instr/INC_LOCK_M32.html says 23 cycles on an 11-
year-old Sandy Bridge, 19 on Haswell, 18 on everything since Skylake.
-- 
Thiago Macieira - thiago.macieira (AT) intel.com
  Cloud Software Architect - Intel DCAI Cloud Engineering



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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-15 Thread Thiago Macieira
On Monday, 14 November 2022 23:52:23 PST Marc Mutz via Development wrote:
> Unless and until we replace all QString/QStringView overload sets with a
> single QAnyStringView function (possibly accompanied by a QString
> Q_WEAK_OVERLOAD), we can't make QString implicitly convertible from
> char16_t literals. While we can QT_REMOVED_SINCE our own API, we can't
> do the same for user API that relies on the explicit guarantee of
> QString/QStringView overloadability.

And then there's user library code. Our changing this would affect them, unless 
they make the same type of change that we do.

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



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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-15 Thread Philippe
On Tue, 15 Nov 2022 08:52:24 +
Marc Mutz via Development  wrote:

> There's nothing inherently Qt-ish about owning containers.

Yes and no, because owning containers are part of the very "Qt-ish"
Implicit Sharing idiom, which one is _great_ for the ease of use, safety
and optimization it provides.

~115 Qt classes: https://doc.qt.io/qt-6/implicit-sharing.html

>QAnyStringView, QUtf8StringView and, later, QAnyString, QUtf8String,
> can be used to to make UTF-8 a first-class citizen in Qt.

Maybe, but I see a deviation from simplicity (not "Qt-ish")

>But UTF-16 is sacrosanct in Qt. It's a cult. Irregardless of how many 
>deep copies it takes to convert to and from UTF-16 from native 
>encodings, people still worship it as god-given. It's not.

If this is "sacrosanct", this is simply because many people appreciate
QString for its rich API and ease of use. This has to be respected.

Philippe

> On 15.11.22 08:14, Ulf Hermann via Development wrote:
> >>> So, if the method immediately converts whatever it gets to QList or
> >>> QString, then there is no point in passing it a span or view.
> >>
> >> My point is that there _is_. Citing my blog post:
> >>
> >>     callConsumeQStringHelloWorld():
> >  > [...]
> > 
> > That's the worst case scenario of passing an 8bit string literal to a 
> > function that takes a QString. We have QStringLiteral to avoid the 8bit 
> > to 16bit conversion, but I know there are more problems with that.
> > 
> > Now lets look at the case of passing a pre-existing QString (i.e. one we 
> > don't have to create in place) to a function taking QAnyStringView and 
> > storing the result as QString.
> > 
> >      // somewhere:
> >      QString a;
> >      void setter(QAnyStringView view) { a = view.toString(); }
> > 
> >      // elsewhere:
> >      QString foo;
> >      [ ... modify foo ... ]
> >      setter(QAnyStringView(foo));
> > 
> > That's a deep copy. A deep copy of a string is obviously more expensive 
> > than the overhead of calling the QString ctor and dtor.
> 
> That remains to be proven. A rule of thumb for atomics is that they're 
> two orders of magnitude slower than a normal int. They also still act as 
> optimizer firewalls. With that rule of thumb, copying 50 char16_t's is 
> faster than one ref-count update. What really is the deciding point is 
> whether or not there's a memory allocation involved. I mentioned that 
> for many use-cases, therefore, a non-CoW SBO container is preferable over a 
> CoW 
> non-SBO one.
> 
> If QString and QByteArray had both efficient substringing and SBO, my 
> arguments would carry much less weight. But we have neither, and adding 
> it (esp. SBO) to QString for Qt 7 will break users, silently. Therefore 
> it's prudent _not_ to change QString, but to add QSmallString or 
> something instead. Until then, u16string and QVLA can be used, 
> by users and implementations.
> 
> Any way you turn it, non-owning containers in the API make for long-term 
> stable APIs even as we improve our container classes in incompatible ways.
> 
> > Which case is 
> > more common? And by what factor?
>  >
> > I can't say what case is more common. What I can say is that the risk of 
> > creating deep copies sounds worse to me than the risk of calling the 
> > QString ctor and dtor too often.
> >
> > This is what I mean with "fuzzy". We don't really have the data to 
> > support a move to QAnyStringView for all of our API.
> 
> I can say with firm belief that, _atm_, passing QString is more common. 
> But this is a self-fulfilling fact. The tst_qsettings experiment shows 
> what can happen if you port an API that doesn't naturally receive 
> pre-made QStrings.
> 
> If you want to take a peek at a world without owning containers, use the 
> Qt 6 QXmlStreamParser API or llvm.
> 
> With the caveat that the problem with QXmlStreamParser is that it needs 
> to convert to UTF-16. The vast majority of XML data is _not_ in UTF-16. 
> If QXmlStreamReader's API was formulated in QAnyStringView, it wouldn't 
> have to. We could just mmap() files and hand out QAnyStringViews to 
> tokens. Not going into whattaboutism here, but what about all those 
> unnecessary encoding conversions? Each one is a deep copy, too. UTF-16 
> is a total alien in the Unix world, and, connecting to your world, I 
> think it's safe to assume that very few QML files are encoded in UTF-16, 
> either. And PySide string are also not encoded in UTF-16, I think.
> 
> But UTF-16 is sacrosanct in Qt. It's a cult. Irregardless of how many 
> deep copies it takes to convert to and from UTF-16 from native 
> encodings, people still worship it as god-given. It's not.
> 
> No-one would use Clang if it internally converted everything to UTF-16 
> first. It would be too slow.
> 
> QAnyStringView, QUtf8StringView and, later, QAnyString, QUtf8String, can 
> be used to to make UTF-8 a first-class citizen in Qt.
> 
> Likewise, owning containers are also sacrosanct in Qt. They're a cult. 
> Irregardless of how 

Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-15 Thread Marc Mutz via Development
On 14.11.22 22:17, Thiago Macieira wrote:
> On Monday, 14 November 2022 12:53:19 PST Marc Mutz via Development wrote:
>>> I don't think we will ever change return types.
>>
>> Your short interjections would be more valuable if you didn't just state
>> an opinion, but also give rationale ;-)
> 
> That's why I said "I think".
> 
> We can't return a non-owning view because that requires that we store
> internally *as* a contiguous area.

The "stored" here is much more of a constraint than the "contiguous". 
Even associative containers can be contiguous (QFlatMap, or a sorted 
vector or struct { key, value }). But I wrote that for non-stored or 
non-contiguous coroutines returning generators can be used. No-one 
suggests to use spans for non-stored or non-contiguous data, so this a 
straw man.

> If we are already doing that, we can store
> as QList and return *as* QList with implicit sharing.

In comparison, returning a QList requires us to store a QList. This is 
objectively a stronger constraint than merely string contiguous data. 
It's also a substantial constraint, as it excludes SBO, like in the 
QRegion case.

> Returning as an iteratable interface requires that we return a proxy object,
> like QRegularExpressionMatch, so that the solution is thread-safe. This is
> neither simple to understand, to code, or to port existing code over to. It
> also requires copying the data over (hopefully, implicitly) to the proxy
> object, so it doesn't solve anything.

I disagree on all points. QREM is complicated because we need to 
shoehorn a coroutine into an iterator concept. Same with 
QStringTokenizer. Coroutines with lazy sequences (generator<>) are very easy to 
implement and use.

If we're to discuss further, please watch my Meeting C++ presentation, 
which lays out all the pros and cons I'm aware of. No need to re-iterate 
them in text here. https://youtu.be/tvdwYwTyrig

See esp. https://youtu.be/tvdwYwTyrig?t=1219 for how trivial a QREMatch 
becomes when coroutines are available.

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] How qAsConst and qExchange lead to qNN

2022-11-15 Thread Marc Mutz via Development
On 15.11.22 08:14, Ulf Hermann via Development wrote:
>>> So, if the method immediately converts whatever it gets to QList or
>>> QString, then there is no point in passing it a span or view.
>>
>> My point is that there _is_. Citing my blog post:
>>
>>     callConsumeQStringHelloWorld():
>  > [...]
> 
> That's the worst case scenario of passing an 8bit string literal to a 
> function that takes a QString. We have QStringLiteral to avoid the 8bit 
> to 16bit conversion, but I know there are more problems with that.
> 
> Now lets look at the case of passing a pre-existing QString (i.e. one we 
> don't have to create in place) to a function taking QAnyStringView and 
> storing the result as QString.
> 
>      // somewhere:
>      QString a;
>      void setter(QAnyStringView view) { a = view.toString(); }
> 
>      // elsewhere:
>      QString foo;
>      [ ... modify foo ... ]
>      setter(QAnyStringView(foo));
> 
> That's a deep copy. A deep copy of a string is obviously more expensive 
> than the overhead of calling the QString ctor and dtor.

That remains to be proven. A rule of thumb for atomics is that they're 
two orders of magnitude slower than a normal int. They also still act as 
optimizer firewalls. With that rule of thumb, copying 50 char16_t's is 
faster than one ref-count update. What really is the deciding point is 
whether or not there's a memory allocation involved. I mentioned that 
for many use-cases, therefore, a non-CoW SBO container is preferable over a CoW 
non-SBO one.

If QString and QByteArray had both efficient substringing and SBO, my 
arguments would carry much less weight. But we have neither, and adding 
it (esp. SBO) to QString for Qt 7 will break users, silently. Therefore 
it's prudent _not_ to change QString, but to add QSmallString or 
something instead. Until then, u16string and QVLA can be used, 
by users and implementations.

Any way you turn it, non-owning containers in the API make for long-term 
stable APIs even as we improve our container classes in incompatible ways.

> Which case is 
> more common? And by what factor?
 >
> I can't say what case is more common. What I can say is that the risk of 
> creating deep copies sounds worse to me than the risk of calling the 
> QString ctor and dtor too often.
>
> This is what I mean with "fuzzy". We don't really have the data to 
> support a move to QAnyStringView for all of our API.

I can say with firm belief that, _atm_, passing QString is more common. 
But this is a self-fulfilling fact. The tst_qsettings experiment shows 
what can happen if you port an API that doesn't naturally receive 
pre-made QStrings.

If you want to take a peek at a world without owning containers, use the 
Qt 6 QXmlStreamParser API or llvm.

With the caveat that the problem with QXmlStreamParser is that it needs 
to convert to UTF-16. The vast majority of XML data is _not_ in UTF-16. 
If QXmlStreamReader's API was formulated in QAnyStringView, it wouldn't 
have to. We could just mmap() files and hand out QAnyStringViews to 
tokens. Not going into whattaboutism here, but what about all those 
unnecessary encoding conversions? Each one is a deep copy, too. UTF-16 
is a total alien in the Unix world, and, connecting to your world, I 
think it's safe to assume that very few QML files are encoded in UTF-16, 
either. And PySide string are also not encoded in UTF-16, I think.

But UTF-16 is sacrosanct in Qt. It's a cult. Irregardless of how many 
deep copies it takes to convert to and from UTF-16 from native 
encodings, people still worship it as god-given. It's not.

No-one would use Clang if it internally converted everything to UTF-16 
first. It would be too slow.

QAnyStringView, QUtf8StringView and, later, QAnyString, QUtf8String, can 
be used to to make UTF-8 a first-class citizen in Qt.

Likewise, owning containers are also sacrosanct in Qt. They're a cult. 
Irregardless of how many deep copies it takes just to call a Qt function 
from Python, Java/Script or QML, people still 
worship them as god-given. They're not.

There's nothing inherently Qt-ish about owning containers. Qt is all 
about allowing developers to write great applications across platforms. 
Ideally, we'd focus on picking up developers where they come from. That 
means instead of inflicting a Java-ish C++ API on Python programmers, we 
make PySide rock by minimizing copies between Python and Qt/C++ data 
structures. iow: NOI.

Guys, take the blinkers off :)

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] How qAsConst and qExchange lead to qNN

2022-11-15 Thread Marc Mutz via Development
On 14.11.22 23:04, A. Pönitz wrote:
>> Marc’s proposal of a Non-Owning Interface is already
>> become manifest in QRegion::begin/end
>>
>> https://doc.qt.io/qt-6/qregion.html#begin
>>
>> allowing us to write
>>
>>
>> for (auto rect : region) doSomethingWith(rect);
> Yes, and that's fine [but not quite matching the rest of the discussion
> of using spans?]
> 
>> (while QRegion::rects will have to create a QList even if there is
>> only a single rect in the inline storage, which is then not a QList).
>>
>> This is a*good*  addition to Qt. I think we can make more such
>> additions to Qt, in places where it makes a real difference for
>> today’s client code using owning containers, and without changing the
>> world.
> Fine with me.
> 
> With emphasis on "addition" and "real", and notable absense of "change"
> and "removal"...

QVector QRegion::rects() const _was_ removed for Qt 6.0.

The trick to treat QRegion as a container of QRect, simplifying both 
users and implementation of QRegion (no more vectorize()), only worked 
because QRegion is home to only one collection. It's also a trick you 
need to see to be able to use it, so discoverability is poor. In the 
general case, a given class may have more than one collection of items. 
E.g. if circular windows became the new rage, QRegion could be a 
collection not just of rects, but also of ellipses. Then what?

Enter span-retuning getters:

for (QRect rect : region.rects())
  ~~~
for (QEllipse ell : recion.ellipses())
  ~~~

Even while we wait for the circles-based GUIs to become en vogue, the 
.rects() is already more discoverable than pure QRegion-as-container.

There's a logical incompatibility between "QRegion as a QRect container 
= good" and "QRection::rects() returning a span of QRect = bad".

Either both are good ideas or neither are. They both grant the same 
freedoms and put the same constraints on the implementation of QRegion, 
but one is more flexible than the other.

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] How qAsConst and qExchange lead to qNN

2022-11-14 Thread Marc Mutz via Development
On 15.11.22 03:32, Giuseppe D'Angelo via Development wrote:
> 
> We've found a robust solution to this problem via the removed_api 
> mechanism, which is going to keep the QString overload, but hide it from 
> clients. (At least, I *think*.) This will make calls passing u"" 
> literals to keep working just fine. So what other concern is there for 
> not allowing this QString construction?

The problem is that replacing a QString function with a QStringView one 
is highly SiC. This has nothing to do with char16_t literals, but more 
with char8_t (and char) literals from which QString implicitly converts, 
but QStringView does not.

This is why we never replaced, but always overloaded, QString functions 
with QStringView ones in Qt 5.

The Qt 6 solution is to replace QString functions with QAnyStringView, 
which is designed to replace an overload set consisting of

- QString
- QChar
- QStringView
- QUtfStringView (incl. char and char8_t literals)
- QLatin1StringView
- QByteArrayView (only for compat, to be subsumed by QUtf8StringView)

with a single function in an SC way.

Unless and until we replace all QString/QStringView overload sets with a 
single QAnyStringView function (possibly accompanied by a QString 
Q_WEAK_OVERLOAD), we can't make QString implicitly convertible from 
char16_t literals. While we can QT_REMOVED_SINCE our own API, we can't 
do the same for user API that relies on the explicit guarantee of 
QString/QStringView overloadability.

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] How qAsConst and qExchange lead to qNN

2022-11-14 Thread Ulf Hermann via Development

So, if the method immediately converts whatever it gets to QList or
QString, then there is no point in passing it a span or view.


My point is that there _is_. Citing my blog post:

callConsumeQStringHelloWorld():

> [...]

That's the worst case scenario of passing an 8bit string literal to a 
function that takes a QString. We have QStringLiteral to avoid the 8bit 
to 16bit conversion, but I know there are more problems with that.


Now lets look at the case of passing a pre-existing QString (i.e. one we 
don't have to create in place) to a function taking QAnyStringView and 
storing the result as QString.


// somewhere:
QString a;
void setter(QAnyStringView view) { a = view.toString(); }

// elsewhere:
QString foo;
[ ... modify foo ... ]
setter(QAnyStringView(foo));

That's a deep copy. A deep copy of a string is obviously more expensive 
than the overhead of calling the QString ctor and dtor. Which case is 
more common? And by what factor?


I can't say what case is more common. What I can say is that the risk of 
creating deep copies sounds worse to me than the risk of calling the 
QString ctor and dtor too often.


This is what I mean with "fuzzy". We don't really have the data to 
support a move to QAnyStringView for all of our API.


best regards,
Ulf
___
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-14 Thread Thiago Macieira
On Monday, 14 November 2022 18:32:36 PST Giuseppe D'Angelo via Development 
wrote:
> I'm not sure what is meant here. I was just pointing out that
> overloading a function with QString and QStringView (or QAnyStringView
> for that matter) is just a historical accident:
> 
> * we have an "old" API that takes QString
> * we realize it doesn't _have to_ (e.g. string never stored, etc.)

Mind you we need to determine not "it doesn't need to store now" but "it will 
never need to store". We have to know ahead of time that we won't need this. 
And if we can't overload, then we need to know this even beyond the next major 
version.

> * we want to replace it with a "newer" API that takes QStringView. We
> can't literally *replace* it (binary, and also, minor, source
> compatibility). So we have to add a QStringView overload and keep both
> for the entire major version.

We can hide the existing one with QT_REMOVED_SINCE. So we can, effectively, 
replace.

> * Now, if we have a QString(const char16_t *) constructor, this
> overloading break SC when someone calls the API with a u"" literal, and
> that's super-annoying.

Right.

> But the point is that if we developed the API from scratch today, we'd
> likely not even had the QString overload to begin with -- we would have
> averted the whole issue.

I don't think that's necessarily true. But that's an orthogonal problem.

Your question remains:

> We've found a robust solution to this problem via the removed_api
> mechanism, which is going to keep the QString overload, but hide it from
> clients. (At least, I *think*.) This will make calls passing u""
> literals to keep working just fine. So what other concern is there for
> not allowing this QString construction?


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



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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-14 Thread Giuseppe D'Angelo via Development

Il 10/11/22 20:27, Thiago Macieira ha scritto:

(We end up with these overload sets because right now we have tons of
f(QString), and we're slowly moving them to f(QStringView) if it does
make sense for them. Due to the BC promise we can't just_remove_
f(QString).)

And we shouldn't. So we shouldn't be moving to QStringView.

Where it's useful, we can use a Q_WEAK_OVERLOAD. But I insist that we only do
that where there's a clear performance benefit. Otherwise, keep using QString.


I'm not sure what is meant here. I was just pointing out that 
overloading a function with QString and QStringView (or QAnyStringView 
for that matter) is just a historical accident:


* we have an "old" API that takes QString
* we realize it doesn't _have to_ (e.g. string never stored, etc.)
* we want to replace it with a "newer" API that takes QStringView. We 
can't literally *replace* it (binary, and also, minor, source 
compatibility). So we have to add a QStringView overload and keep both 
for the entire major version.
* Now, if we have a QString(const char16_t *) constructor, this 
overloading break SC when someone calls the API with a u"" literal, and 
that's super-annoying.



But the point is that if we developed the API from scratch today, we'd 
likely not even had the QString overload to begin with -- we would have 
averted the whole issue.


We've found a robust solution to this problem via the removed_api 
mechanism, which is going to keep the QString overload, but hide it from 
clients. (At least, I *think*.) This will make calls passing u"" 
literals to keep working just fine. So what other concern is there for 
not allowing this QString construction?




Thanks,

--
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] How qAsConst and qExchange lead to qNN

2022-11-14 Thread A . Pönitz
On Mon, Nov 14, 2022 at 04:54:20PM +, Volker Hilsheimer wrote:
> 
> > On 12 Nov 2022, at 14:41, A. Pönitz  wrote:
> > 
> > On Fri, Nov 11, 2022 at 09:35:27AM +0100, Ulf Hermann via
> > Development wrote:
> >> There is an undeniable benefit of _offering_ QSpan, QStringView,
> >> and generator APIs in a few relevant cases:
> > 
> > This is true, but my problem with this is that already _offering_
> > additional solutions does not come for free: It bloats the API, it
> > bloats docs, introducing the first overload is SIC etc.
> 
> 
> With QT_REMOVED_FROM we can remove the “first overload” problem, and
> replace QString with QAnyStringView.
> 
> We could use QAnyStringView  in e.g.. QFont::fromString or
> QPageRanges::fromString or QKeySequence::fromString (the obvious
> candidates in Qt Gui). And then I can write either of
> 
> 
> fromString(“foo”); fromString(u“foo”); fromString(u"foo”_s);
> fromString(stdString); fromString(qString);
> 
> Is that not a good idea?

Judging from the frequency of this kind of operation in "my" code, my
answer is "No": The main issue is "Opportunity cost": The whole activity
is - at best - neutral. This kind of optimization has no end-user-visible
effect, even when scaling to a hundred uses per day. On the other side,
it eats resources, preventing to fix real, user-visible problems
(including /performance/ btw).

[Besides: Wasn't it that QAnyStringView drops the QString reference
count, i.e. even /pessimizes/ the so-far not-so-uncommon way of
operating on pre-existing QString objects?]

> Perhaps it isn’t because there is now a fraction of our API that I can
> call that way, why for the vast majority I have to always use
> Qt::StringLiterals and call with a u”…”_s.

No. You don't /have/ to, it is your choice. You can just use the
QString you have, or one you create ad-hoc, and no end-user will notice.
For Qt itself, it might be prudent to use the prefered string decoration
du jour for the implementation, but on an /application/ level outside
really heavy duty code, there's no need to even add a 'u' to a string
literal. Yes, there will by copies, yes, there will be allocation, and
no, people won't notice. And environment-wise there will be more cycles
burnt on creating, and reviewing, and integrating the Qt patch.

> But how can we make progress with our API otherwise? Should we accept
> that we will never be able to call a QString-taking setter with u”…”?

"Making changes, just because we can" is no progress. As written in
another mail recently, I'd pretty much prefer if Qt API gets only
changed if there's are real mistake to fix (nowadays, with most Qt 5
code not yet ported, actually a "after paddling back on some of the
Qt5->6 changes").

> [...]
> I don’t think we can be certain that the amount of client code that
> use non-Qt containers and only deals with Qt containers at the API
> boundary is negligible. I would rather assume that only a minority of
> Qt applications *only* use Qt. The slice of the world that truly is a
> “Qt World” is perhaps rather small, while the majority of applications
> is a messy, organically grown mix of different frameworks and
> libraries. In those, most thing are alien to most other things, and
> interoperability on various levels is important.

The main operation of any backend with Qt is "using Qt as a GUI", with
mostly high-level building blocks on the Qt side. Creating a copy of a
string to interface such a block, like rendering this string on a
display, or specifying a font, or a key sequence does not have to be
micro-optimized _when this otherwise impacts API_. And it does not even
have to be micro-optimized when it does _not_ impact API, see
"opportunity cost".

> So making it convenient for client code to use Qt APIs without having
> to deal with Qt containers has value.

Very limited value in my book.

> It is possible today - although
> perhaps under-documented, esp since we removed to/fromStdVector and
> to/fromStdList from Qt 6:
> 
> setList({svector.cbegin(), svector.cend()});

[Btw: There is a self-fulfilling prophecy regarding the badness of Qt's
or generally owning containers here: By repeating the claim that e.g.
QList is bad or that full-container operations are bad, or anything that
is not the equivalent of std::containers is bad, the seed was planted to
remove some of Qt container's intrinsic benefits (ease of use...) or at
Qt5->6 even full classes.]

> This is not great, but as long as client code is anyway operating on
> owning containers, it’s perhaps as good as we can reasonably make it.

The "improvement" of having to use

  setList({svector.cbegin(), svector.cend()});

already struck back at peoply converting 

  setList(functionReturningList())

to

  setList({functionReturningContainer().begin(),
   functionReturningContainer().end())}

Being able to pass around owning containers cheaply / without having to
worry about performace in each line is one of the benefits of /Qt style/
interface.

> > The 

Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-14 Thread Thiago Macieira
On Monday, 14 November 2022 12:53:19 PST Marc Mutz via Development wrote:
> > I don't think we will ever change return types.
> 
> Your short interjections would be more valuable if you didn't just state
> an opinion, but also give rationale ;-)

That's why I said "I think".

We can't return a non-owning view because that requires that we store 
internally *as* a contiguous area. If we are already doing that, we can store 
as QList and return *as* QList with implicit sharing.

Returning as an iteratable interface requires that we return a proxy object, 
like QRegularExpressionMatch, so that the solution is thread-safe. This is 
neither simple to understand, to code, or to port existing code over to. It 
also requires copying the data over (hopefully, implicitly) to the proxy 
object, so it doesn't solve anything.

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



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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-14 Thread Marc Mutz via Development
On 14.11.22 18:11, Thiago Macieira wrote:
> On Monday, 14 November 2022 08:22:44 PST Marc Mutz via Development wrote:
>> Can we agree that NOI for setters is a no-brainer? Then 90% of the
>> usefulness of NOI can already be reaped, in a BC and SC manner. There's
>> pretty little we can do with return values before Qt 7, except use the
>> stuff in private APIs to try it out. But we can and should convert
>> setters already.
> 
> Not without conditions.
> 
> I can agree with that so long as it doesn't remove the ability to add the
> owning equivalent as overload, even if it is a Q_WEAK_OVERLOAD, where it
> benefits because the target code is going to store anyway.

_If_ and for as long as the class stores the data in _that_ owning 
container, fine by me. That'll probably be the case for Qt 6 going 
forward, because of those pesky owning container return types.

That said, I repeat that the goal here needs to be to let each class 
choose its own optimal data structure, and for a large class of cases 
that would be QVLA/llvm::SmallVector, iow: _something_ with a small 
buffer optimisation.

As long as we have an implicitly-shared everywhere policy, it's CoW on 
top of CoW on top of CoW. If the QRegionData/Private is ref-counted, 
then it makes little sense to contain the QRects in a container that's 
_also_ CoW. Nothing is using the implict sharedness of the QRect 
container (rects() is gone in 6.0, and can be re-used for a QSpan getter).

> I don't think we will ever change return types.

Your short interjections would be more valuable if you didn't just state 
an opinion, but also give rationale ;-)

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] How qAsConst and qExchange lead to qNN

2022-11-14 Thread Volker Hilsheimer via Development


> On 14 Nov 2022, at 18:30, Marc Mutz via Development 
>  wrote:
> 
> Hi Ulf,
> 
> On 14.11.22 17:37, Ulf Hermann via Development wrote:
>> Hi Marc,
>> 
>>> On 11.11.22 09:35, Ulf Hermann via Development wrote:
 There is an undeniable benefit of _offering_ QSpan, QStringView, and
 generator APIs in a few relevant cases:
 
 1. Users want to pass a "foreign" container to a Qt function that
 doesn't only store it as QList or QString. It might merely iterate it or
 store it as something else.
>>> 
>>> The assumption that there's a problem only for "foreign containers" is
>>> incorrect: Take the native Qt container QString as an example. See
>>> assembly in my QAnyStringView blog post:
>>> https://www.qt.io/blog/qstringview-diaries-qanystringview You have this
>>> problem as soon as you pass constant data, which is a common enough
>>> use-case to warrant optimizing for.
>> 
>> My point is the "doesn't only store it as QList or QString" and 
>> "foreign" is in quotes quite on purpose. Maybe that wasn't clear enough, 
>> though.
>> 
>> So, if the method immediately converts whatever it gets to QList or 
>> QString, then there is no point in passing it a span or view.
> 
> My point is that there _is_. Citing my blog post:

[…]

> It matters whether the QString/QList creation is in front of or behind 
> the ABI boundary. The size of the tst_qsettings executable for Clang 
> builds went down by >10% when the API was switched over to 
> QAnyStringView, without any changes to the test code itself (and 
> QAnyStringView hasn't even got the size/tag swap that Thiago pointed out 
> and that would make it require less gargantuan immediate values):
> https://codereview.qt-project.org/c/qt/qtbase/+/353688
> 
> Sure, the sequence of assembler instructions is the same, but improved 
> code locality means it'll also execute a bit faster. Also, if we can 
> condense user code by 10% without changes to their code, then some users 
> may have an easier time right-sizing their embedded hardware, e.g.
> 
> 
> Finally, to give you the very big picture: we have a duty to avoid 
> inflicting such egregiously inefficient code on the world. This bloat 
> needs to be stored: on disk, in RAM, in cache. At each of these levels, 
> the extra storage causes some pJ extra energy use, billions of times 
> over, causing corresponding CO2 emissions.
> 
> The German government has started dealing out the well-known "blue 
> angel" certificates for software now. Customers will eventually demand 
> our help in getting the energy use of their software down so they can be 
> certified.
> 
> We can, of course, ostrich on. But Qt is in a unique position, as a C++ 
> native UI framework, to assist users in meeting climate goals. Think 
> about it: Would projects that prefer programmer productivity over 
> resource use inflict C++ on their programmers? Won't such projects 
> rather (continue to) use Java and HTML, shipping applications with a 
> bundled chromium to run a few HTML/JS apps in GiBs of memory instead of 
> MiBs (hello, MS Teams!)? To me, it sounds more logical that projects 
> that inflict C++ on their developers would be those projects that need 
> to meet resource usage goals (because of right-sizing of embedded 
> hardware or because they want to get certified as energy-efficient). And 
> all this talk about convenience trumping efficiency is going to raise 
> eyebrows in such projects.
> 
> Let's make C++ easy to use, for sure. But, sorry, using spans isn't 
> difficult. Don't confuse familiarity with simplicity.
> 


Hey Marc,


Today, the vast majority of client code already has an owning container that 
gets passed around by copy or as const references. In Qt, we usually store data 
in the implementation in QList and QString. A test case that artificially 
creates a ton of QString instances perhaps rather falls into the small and 
isolated use case that doesn’t translate very well into real applications.

There are most certainly classes where it’d be good if we could replace that 
implementation with e.g. a std::pmr::vector with an optimised allocator. And 
then being stuck with a QList API forces both us and client code to construct 
suboptimal data structures. And there are APIs where replacing the QString 
version with QAnyStringView makes perfect sense (such as all remaining 
fromString factory functions).

But that we either replace all, or none of our APIs with something taking a 
view or a span are perhaps not the only outcomes of this conversation.

Can we focus on the cases with the biggest wins, like you already did with 
QRegion, QSettings, and QObject::setObjectName? What APIs in Qt that take a 
QString are usually called with a string literal in real applications, rather 
than with an already created QString object (that is in turn the result of user 
input or reading from some storage)? What was the outcome of the 
QObject::setObjectName change for e.g. Qt Creator?

What would a 

Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-14 Thread Marc Mutz via Development
Hi Ulf,

On 14.11.22 17:37, Ulf Hermann via Development wrote:
> Hi Marc,
> 
>> On 11.11.22 09:35, Ulf Hermann via Development wrote:
>>> There is an undeniable benefit of _offering_ QSpan, QStringView, and
>>> generator APIs in a few relevant cases:
>>>
>>> 1. Users want to pass a "foreign" container to a Qt function that
>>> doesn't only store it as QList or QString. It might merely iterate it or
>>> store it as something else.
>>
>> The assumption that there's a problem only for "foreign containers" is
>> incorrect: Take the native Qt container QString as an example. See
>> assembly in my QAnyStringView blog post:
>> https://www.qt.io/blog/qstringview-diaries-qanystringview You have this
>> problem as soon as you pass constant data, which is a common enough
>> use-case to warrant optimizing for.
> 
> My point is the "doesn't only store it as QList or QString" and 
> "foreign" is in quotes quite on purpose. Maybe that wasn't clear enough, 
> though.
> 
> So, if the method immediately converts whatever it gets to QList or 
> QString, then there is no point in passing it a span or view.

My point is that there _is_. Citing my blog post:

   callConsumeQStringHelloWorld():
 pushq   %rbp
 movl$12, %esi
 leaq.LC76(%rip), %rdx
 subq$32, %rsp
 movq%rsp, %rbp
 movq%rbp, %rdi
 callQString::fromUtf8(QByteArrayView)@PLT
 movq%rbp, %rdi
 callconsumeQString(QString const&)@PLT
 movq(%rsp), %rax
 testq   %rax, %rax
 je  .L834
 lock subl   $1, (%rax)
 je  .L839
   .L834:
 addq$32, %rsp
 popq%rbp
 ret
   .L839:
 movq(%rsp), %rdi
 movl$8, %edx
 movl$2, %esi
 callQArrayData::deallocate(QArrayData*, long long, long 
long)@PLT
 addq$32, %rsp
 popq%rbp
 ret

   callConsumeQStringViewHelloWorld():
 leaq.L.str.3401(%rip), %rsi
 movl$12, %edi
 jmp consumeQStringView(QStringView)@PLT # TAILCALL


It matters whether the QString/QList creation is in front of or behind 
the ABI boundary. The size of the tst_qsettings executable for Clang 
builds went down by >10% when the API was switched over to 
QAnyStringView, without any changes to the test code itself (and 
QAnyStringView hasn't even got the size/tag swap that Thiago pointed out 
and that would make it require less gargantuan immediate values):
https://codereview.qt-project.org/c/qt/qtbase/+/353688

Sure, the sequence of assembler instructions is the same, but improved 
code locality means it'll also execute a bit faster. Also, if we can 
condense user code by 10% without changes to their code, then some users 
may have an easier time right-sizing their embedded hardware, e.g.


Finally, to give you the very big picture: we have a duty to avoid 
inflicting such egregiously inefficient code on the world. This bloat 
needs to be stored: on disk, in RAM, in cache. At each of these levels, 
the extra storage causes some pJ extra energy use, billions of times 
over, causing corresponding CO2 emissions.

The German government has started dealing out the well-known "blue 
angel" certificates for software now. Customers will eventually demand 
our help in getting the energy use of their software down so they can be 
certified.

We can, of course, ostrich on. But Qt is in a unique position, as a C++ 
native UI framework, to assist users in meeting climate goals. Think 
about it: Would projects that prefer programmer productivity over 
resource use inflict C++ on their programmers? Won't such projects 
rather (continue to) use Java and HTML, shipping applications with a 
bundled chromium to run a few HTML/JS apps in GiBs of memory instead of 
MiBs (hello, MS Teams!)? To me, it sounds more logical that projects 
that inflict C++ on their developers would be those projects that need 
to meet resource usage goals (because of right-sizing of embedded 
hardware or because they want to get certified as energy-efficient). And 
all this talk about convenience trumping efficiency is going to raise 
eyebrows in such projects.

Let's make C++ easy to use, for sure. But, sorry, using spans isn't 
difficult. Don't confuse familiarity with simplicity.

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] How qAsConst and qExchange lead to qNN

2022-11-14 Thread Thiago Macieira
On Monday, 14 November 2022 08:22:44 PST Marc Mutz via Development wrote:
> Can we agree that NOI for setters is a no-brainer? Then 90% of the
> usefulness of NOI can already be reaped, in a BC and SC manner. There's
> pretty little we can do with return values before Qt 7, except use the
> stuff in private APIs to try it out. But we can and should convert
> setters already.

Not without conditions.

I can agree with that so long as it doesn't remove the ability to add the 
owning equivalent as overload, even if it is a Q_WEAK_OVERLOAD, where it 
benefits because the target code is going to store anyway.

I don't think we will ever change return types.

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



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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-14 Thread Volker Hilsheimer via Development

> On 12 Nov 2022, at 14:41, A. Pönitz  wrote:
> 
> On Fri, Nov 11, 2022 at 09:35:27AM +0100, Ulf Hermann via Development wrote:
>> There is an undeniable benefit of _offering_ QSpan, QStringView, and
>> generator APIs in a few relevant cases:
> 
> This is true, but my problem with this is that already _offering_
> additional solutions does not come for free: It bloats the API,
> it bloats docs, introducing the first overload is SIC etc.


With QT_REMOVED_FROM we can remove the “first overload” problem, and replace 
QString with QAnyStringView.

We could use QAnyStringView  in e.g.. QFont::fromString or 
QPageRanges::fromString or QKeySequence::fromString (the obvious candidates in 
Qt Gui). And then I can write either of


fromString(“foo”);
fromString(u“foo”);
fromString(u"foo”_s);
fromString(stdString);
fromString(qString);

Is that not a good idea?

Perhaps it isn’t because there is now a fraction of our API that I can call 
that way, why for the vast majority I have to always use Qt::StringLiterals and 
call with a u”…”_s.

But how can we make progress with our API otherwise? Should we accept that we 
will never be able to call a QString-taking setter with u”…”?


>> 1. Users want to pass a "foreign" container to a Qt function that doesn't
>> only store it as QList or QString. It might merely iterate it or store it as
>> something else.
> 
> Tradionally, we had QList as the (designed to be, and in practice also
> being) the "usually good enough for most uses" container, so in a /Qt/
> World, this "alien container" case /practically/ had no relevance,


I don’t think we can be certain that the amount of client code that use non-Qt 
containers and only deals with Qt containers at the API boundary is negligible. 
I would rather assume that only a minority of Qt applications *only* use Qt. 
The slice of the world that truly is a “Qt World” is perhaps rather small, 
while the majority of applications is a messy, organically grown mix of 
different frameworks and libraries. In those, most thing are alien to most 
other things, and interoperability on various levels is important.

So making it convenient for client code to use Qt APIs without having to deal 
with Qt containers has value. It is possible today - although perhaps 
under-documented, esp since we removed to/fromStdVector and to/fromStdList from 
Qt 6:

setList({svector.cbegin(), svector.cend()});

This is not great, but as long as client code is anyway operating on owning 
containers, it’s perhaps as good as we can reasonably make it.


> The problem of regularly having to convert between Qt containers has been
> /introduced/ by people advocating QList uses by QVector, or std::vector.


Qt 3’s QValueList was implicitly constructible from std::list. We added it 
because people asked for easier integration of Qt with STL-using code.


>> 2. Assume a container that isn't internally stored as QList or QString, but
>> is returned from a Qt function. Users want to use that thing as something
>> else than QString or QList. For example they might merely iterate it or
>> store its contents in a "foreign" container.
>> 
>> In those cases, using QList or QString as transfer mechanism induces an
>> unnecessary deep copy.
> 
> This is pretty much the same problem: Standardizing on QString and QList
> as the "primary" containers avoids these problems on a large scale,
> and this overall gain outweighs the effect local micro-optimizations
> by far. The problem is that this (un-)balance is hard to communicate,
> as it is very easy to demonstrate that small, isolated uses of QString
> and QList are suboptimal, but the /overall/ benefit of a uniform
> approach only kicks in at "real world"-sized applications.



For applications that are not “pure Qt" applications - and I believe that is 
not an insignificant number, perhaps it’s even the majority - standardizing on 
Qt data types is simply not an option.

And Ulf’s case here is anyway that “there is no QList or QString” that is 
internally stored and that we just need to return. There might not even be any 
list-or string-like datastructure stored.

QItemSelectionModel doesn’t store a QList of selected indices. And yet 
QItemSelectionModel::selectedIndexes returns a QModelIndexList that is rather 
costly to create. An application that just wants to iterate over all selected 
indexes already has to pay an unnecessary cost. An application that then 
doesn’t use Qt containers in client code pays an extra cost on top.

I think it would be good if we could develop an API strategy that avoids that. 
Marc’s proposal of a Non-Owning Interface is already become manifest in 
QRegion::begin/end

https://doc.qt.io/qt-6/qregion.html#begin

allowing us to write


for (auto rect : region)
   doSomethingWith(rect);


(while QRegion::rects will have to create a QList even if there is only a 
single rect in the inline storage, which is then not a QList).


This is a *good* addition to Qt. I think we can make more such 

Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-14 Thread Ulf Hermann via Development

Hi Marc,


On 11.11.22 09:35, Ulf Hermann via Development wrote:

There is an undeniable benefit of _offering_ QSpan, QStringView, and
generator APIs in a few relevant cases:

1. Users want to pass a "foreign" container to a Qt function that
doesn't only store it as QList or QString. It might merely iterate it or
store it as something else.


The assumption that there's a problem only for "foreign containers" is
incorrect: Take the native Qt container QString as an example. See
assembly in my QAnyStringView blog post:
https://www.qt.io/blog/qstringview-diaries-qanystringview You have this
problem as soon as you pass constant data, which is a common enough
use-case to warrant optimizing for.


My point is the "doesn't only store it as QList or QString" and 
"foreign" is in quotes quite on purpose. Maybe that wasn't clear enough, 
though.


So, if the method immediately converts whatever it gets to QList or 
QString, then there is no point in passing it a span or view. Otherwise, 
since we don't know what people are going to do with the method (pass 
foreign containers, subsets, etc), a span or view may be adequate.


br,
Ulf
___
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-14 Thread Marc Mutz via Development
Hi Ulf,

On 11.11.22 09:35, Ulf Hermann via Development wrote:
> There is an undeniable benefit of _offering_ QSpan, QStringView, and 
> generator APIs in a few relevant cases:
> 
> 1. Users want to pass a "foreign" container to a Qt function that 
> doesn't only store it as QList or QString. It might merely iterate it or 
> store it as something else.

The assumption that there's a problem only for "foreign containers" is 
incorrect: Take the native Qt container QString as an example. See 
assembly in my QAnyStringView blog post: 
https://www.qt.io/blog/qstringview-diaries-qanystringview You have this 
problem as soon as you pass constant data, which is a common enough 
use-case to warrant optimizing for.

Aside: I find it amusing when people complain that span<>s aren't as 
optimal as (ptr, n) on the broken Windows ABI, but then say that all 
APIs should take owning containers instead. Either we care about 
effciency of argument passing, or not. IMHO, spans strike the optimal 
balance between efficiency and convenience.

The next major problem is subsetting. The Qt containers (QString, 
QByteArray, QList) don't have efficient subsetting. Any form of parsing 
thus directly benefits from views, and the end of parsing will then 
naturally be tokens stored in views. If relevant consumer APIs don't 
take views, but owning containers, you're likewise injecting owning 
container constructions in user code, let alone memory allocations. A 
good API should not have such impedance mismatches between its parsers 
and its data consumers. And please, don't shoot the messenger. 
QStringRef was trying to solve the same problem, just badly (being tied 
to QString without being able to produce a QString from a substring w/o 
deep copy).

Yes, there's fromRawData(), but it doesn't remove the ctor and dtor 
calls of the owning container. And apart from QStringLiteral, no-one uses it. 
So even if the class stores data in 
native containers, the construction of these native containers is often 
better done centrally instead of being duplicated in each caller.

To summarize: the deep copy often happens already, in user code. By 
using spans, the deep copy still happens, but the code to do so isn't 
duplicated.

> All other cases look much fuzzier to me. QSpan or QStringView (or a 
> generator) may be beneficial or detrimental there, depending on exact 
> usage pattern. The cost we're avoiding is mostly the reference count
 > a far cry from a deep copy.

This is not correct. The ref count of owning containers is certainly not 
the main reason to use views. Otherwise non-implicitly-shared containers 
would be a solutions, which they are not.

As detailed above, we're mainly avoiding the code bloat of owning 
container construction and destruction, as well as the deep-copy on 
subsetting. We also avoid accidental detaches

for (auto [pos, col] : gradient.stops())
  doSomthingWith(pos, col);

and allow the implementation of a class to choose an optimal data 
structure without causing an impedance mismatch with its own API.

I find it amusing when some people say that the use of NOI constrains 
implementations when the opposite is demonstrably true (QRegion).

Both owning and non-owning interfaces have their advantages and 
disadvantages. Huge code bases like llvm show that you can program just 
as well with NOI (ArrayRef/StringRef) as with owning containers.

And I think at least for setters, the benefits of NOI far outweigh the 
drawbacks. The only drawback I have heard is deep copy. From the same 
people that suggest to favour convenience over efficiency. Well, for 
setters, nothing is more convenient than NOI.

Can we agree that NOI for setters is a no-brainer? Then 90% of the 
usefulness of NOI can already be reaped, in a BC and SC manner. There's 
pretty little we can do with return values before Qt 7, except use the 
stuff in private APIs to try it out. But we can and should convert 
setters already.

> On the flip side we're introducing complex 
> life time problems that will lead to hard to find memory management 
> defects.

Spans add no lifetime problems on top of .data() and/or .begin()/.end(). 
In particular, these are statically detectable (cf. 
https://github.com/isocpp/CppCoreGuidelines/blob/master/docs/Lifetime.pdf, 
implemented in VS 2019 and (experimentally) in Clang), so they're not 
"hard to find".

> I suggest we look at this from the perspective of a _user_ of 
> Qt. I'm pretty sure you can all imagine which problem a user would 
> prefer here.
 >
> So, I suggest we add those "view" APIs to the cases where they provide a 
> clear benefit. For methods that return a span/view/generator, we should 
> always offer a safe alternative that returns an owning container. The 
> naming convention should be:
> 
> 1. Use overloads for methods that take views or spans. In new API we can 
> omit the methods that take owning containers. If the overload set grows 
> out of hand, don't add the view/span alternative 

Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-13 Thread Jaroslaw Kobus via Development
> 1. Use overloads for methods that take views or spans. In new API we can
> omit the methods that take owning containers. If the overload set grows
> out of hand, don't add the view/span alternative until we can remove
> something. By Thiago's argument, that means not to convert existing
> methods to QStringView for now.
> 
> 2. Use the postfix "View", "Span" or "Generator" for methods that return
> views, spans or generators rather than owning containers. This way it's
> harder for users to mess up the life time.

Ad 1. Having overloads sometimes causes issues, like e.g. can't be a slot due to
ambiguity when making a signal-slot connection. For this reason I believe that
most (all?) of signals overloads were removed in Qt 6. The similar issues may
be encountered when using QtConcurrent API.

So, if you suggest suffix for Ad 2, maybe make it consistent and use the same 
suffix for Ad 1, too?

Disclaimer: I'm not saying that adding "view" and "span" functionality into Qt 
is a good idea.
It's a bit like giving a raw pointer of some part of your internal data into 
the public API
(however, wrapped nicely with a fashionable envelope - so it shouldn't look so 
bad, right?).
Yeah, in some cases it may save 1 ns for containers having thousands of items. 
But will it make
e.g. rendering QGradient faster (when gradient stops are wrapped with span, as 
Mark suggested)?
Especially, as it was mentioned, typical case is 2, max 3 items?

Qt was always cute, because its API was designed so that it's really hard to 
mess things up.
It looks like we are slowly dropping this beautiful keynote, unfortunately.

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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-13 Thread A . Pönitz
On Fri, Nov 11, 2022 at 09:35:27AM +0100, Ulf Hermann via Development wrote:
> There is an undeniable benefit of _offering_ QSpan, QStringView, and
> generator APIs in a few relevant cases:

This is true, but my problem with this is that already _offering_
additional solutions does not come for free: It bloats the API,
it bloats docs, introducing the first overload is SIC etc.

> 1. Users want to pass a "foreign" container to a Qt function that doesn't
> only store it as QList or QString. It might merely iterate it or store it as
> something else.

Tradionally, we had QList as the (designed to be, and in practice also
being) the "usually good enough for most uses" container, so in a /Qt/
World, this "alien container" case /practically/ had no relevance,

The problem of regularly having to convert between Qt containers has been
/introduced/ by people advocating QList uses by QVector, or std::vector.

> 2. Assume a container that isn't internally stored as QList or QString, but
> is returned from a Qt function. Users want to use that thing as something
> else than QString or QList. For example they might merely iterate it or
> store its contents in a "foreign" container.
>
> In those cases, using QList or QString as transfer mechanism induces an
> unnecessary deep copy.

This is pretty much the same problem: Standardizing on QString and QList
as the "primary" containers avoids these problems on a large scale,
and this overall gain outweighs the effect local micro-optimizations
by far. The problem is that this (un-)balance is hard to communicate,
as it is very easy to demonstrate that small, isolated uses of QString
and QList are suboptimal, but the /overall/ benefit of a uniform
approach only kicks in at "real world"-sized applications.

[Within a function, or a private class, or to access external functions
it's of course fine to use anything that fits and this can be
micro-optimized at will]

> All other cases look much fuzzier to me. QSpan or QStringView (or a
> generator) may be beneficial or detrimental there, depending on exact usage
> pattern.

Right.

> The cost we're avoiding is mostly the reference count, a far cry
> from a deep copy. On the flip side we're introducing complex life time
> problems that will lead to hard to find memory management defects. I suggest
> we look at this from the perspective of a _user_ of Qt. I'm pretty sure you
> can all imagine which problem a user would prefer here.
> So, I suggest we add those "view" APIs to the cases where they provide a
> clear benefit.

Right.

But we should to spell out what "clear benefits" means. I /my/ world it
would not be sufficient to give examples of cases where it helps, but
also need weighing with the expected frequency and against the
(potentially, not necessarily exitsting) costs for other/"normal" users.

> For methods that return a span/view/generator, we should
> always offer a safe alternative that returns an owning container. The naming
> convention should be:
> 
> 1. Use overloads for methods that take views or spans.uIn new API we can
> omit the methods that take owning containers.

I am not sure that in the typical case a span-only is a benefit. As said
before, this leaks implementation details into the API and pessimizes
the case where the user /has/ a container.

> If the overload set grows out
> of hand, don't add the view/span alternative until we can remove something.

I oppose any scheme that makes removal of API a common case or part of
the overall scheme.

> By Thiago's argument, that means not to convert existing methods to
> QStringView for now.

That at least, right.
 
> 2. Use the postfix "View", "Span" or "Generator" for methods that return
> views, spans or generators rather than owning containers. This way it's
> harder for users to mess up the life time.

That's fine.

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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-11 Thread Ulf Hermann via Development
There is an undeniable benefit of _offering_ QSpan, QStringView, and 
generator APIs in a few relevant cases:


1. Users want to pass a "foreign" container to a Qt function that 
doesn't only store it as QList or QString. It might merely iterate it or 
store it as something else.


2. Assume a container that isn't internally stored as QList or QString, 
but is returned from a Qt function. Users want to use that thing as 
something else than QString or QList. For example they might merely 
iterate it or store its contents in a "foreign" container.


In those cases, using QList or QString as transfer mechanism induces an 
unnecessary deep copy.


All other cases look much fuzzier to me. QSpan or QStringView (or a 
generator) may be beneficial or detrimental there, depending on exact 
usage pattern. The cost we're avoiding is mostly the reference count, a 
far cry from a deep copy. On the flip side we're introducing complex 
life time problems that will lead to hard to find memory management 
defects. I suggest we look at this from the perspective of a _user_ of 
Qt. I'm pretty sure you can all imagine which problem a user would 
prefer here.


So, I suggest we add those "view" APIs to the cases where they provide a 
clear benefit. For methods that return a span/view/generator, we should 
always offer a safe alternative that returns an owning container. The 
naming convention should be:


1. Use overloads for methods that take views or spans. In new API we can 
omit the methods that take owning containers. If the overload set grows 
out of hand, don't add the view/span alternative until we can remove 
something. By Thiago's argument, that means not to convert existing 
methods to QStringView for now.


2. Use the postfix "View", "Span" or "Generator" for methods that return 
views, spans or generators rather than owning containers. This way it's 
harder for users to mess up the life time.


best regards,
Ulf
___
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-10 Thread A . Pönitz
On Thu, Nov 10, 2022 at 03:55:39PM +, Marc Mutz via Development wrote:
> Hi Ivan,
> 
> On 10.11.22 11:03, Ivan Solovev via Development wrote:
> > I wonder how your NOI-everywhere suggestion will work with the 
> > signal/slot connections? Specially for the case when 
> > Qt::QueuedConnection is used (explicitly or implicitly).
> 
> That's a very good question, thanks.
> 
> As a first reaction, three things come to mind:
> 
> 1. It doesn't, obviously. If you emit signals, any signal argument must
> be owning, or QueuedConnection cannot be used. Given that C++20
> requires us to mark up views and non-owning ranges (enable_view,
> enable_borrowed_range), I'm confident that we could detect attempts
> to create such connections at compile- or, latest, at run-time, and
> refuse them, or, since we need to serialize the arguments into a
> QMetaCallEvent, anyway, also lifetime-pin, either by holding a weak
> reference to the source object or by storing the data in the event in
> an owning container.
> 
> 2. QML and QProperty do not appear to require passing the new value in a
> NOTIFY signals's parameter, so just not passing the argument is an
> option, too (but doesn't work with QueuedConnection, either).
> 
> 3. Finally, the kinds of types I'm primarily thinking of in the context
> of NOI are not Q_OBJECTS. It remains to be seen in which form or
> shape co-routine-based reactive programming will complement or even
> substitute signal/slots in the future.
> 
> So, to summarize, I'd say that NOI works well with signal/slot, too, 
> with the exception of QueuedConnection, where we have several options 
> for work-arounds.

In the context of Qt, a library, that is used by code in unrelated
projects, which may or may not use QueuedConnection on each signal,
in fact for each individual connect, I cannot even remotely think
of a non-owning interface unless this is somehow "owning in disguise",
i.e. offer spans as parameters but effectively stashs away a deep copy
in a safe place.

Could you give a hint on what these several options for work-arounds
may be?

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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-10 Thread A . Pönitz


[

TL;DR :

Qt API should use

   QString foo() const;
   void setFoo(const QString &);

Neither QStringView nor C++20 changes this.

]


On Wed, Nov 09, 2022 at 10:52:15AM +, Marc Mutz via Development wrote:
> Hi Ulf,
> 
> On 09.11.22 08:18, Ulf Hermann via Development wrote:
> >> I don't want to take the Qt containers away from Qt users. I want to get
> >> rid of their use in our APIs, so that both the Qt implementation as well
> >> as our users are free to choose the best container for their needs
> >> instead of having to pick from one of the public Qt containers.
> >
> > I would like to know how that is supposed to work in practice. We have a 
> > lot of public API dealing with Qt containers all over. What are you 
> > going to do to, for example, to
> > 
> >  void addActions(const QList );
> > 
> > in qwidget.h? What should it look like when we're done and our users are 
> > free to choose the best container for their needs?
> 
> In this particular case, the API would be
> 
> void addActions(QSpan actions);
> 
> This is the trivial case: setters for contiguous data.

This might be trivial to implement, but the result is not obviously
better.

In case the actions would directly be stored in a QList
this suddenly causes a full deep copy of the list.

Even if we would apply this pattern only to cases where the contents
not stored as such, this introduces several problems:
 
 (a) we get inconsistent API, function signatures differ without
 obvious user-visible reason,
 (b) the signatures depend on the implemenations, i.e. details
 of the implementation now leak into the API,
 (c) the implementations cannot change freely anymore as 
 "necessary" changes to the signatures cannot be done due
  to the API
 (d) is gets spooky when a function takes several string args
 and needs a copy of some, but not of all

> It's backwards-compatible, because QList implicitly converts to QSpan (and 
> std::span), but now, as a user, I can also pass a C array or std::vector 
> or QVLA of QAction*:
> 
> QAction *actions[] = {
>new FooAction(this),
>new BarAction(this),
> };
> addActions(actions);

Qt applications are using, well, Qt. People are of course free to store
QActions in a std::vector when they dislike QList, but of the end of the
day I very much prefer if people with 

> This is the same effect we have with Q*StringView, incl. avoidance of 
> caller-side construction and destruction of owning containers.

It's the same effect, but the frequency of the need of a caller side
construction of the owning container depend on the caller. Most notably,
the caller code can even influence that by using the "right" container
to start with. The "problem" you expect here only appears for code that
decides to deviate from Qt style, as soon as you are consistent, it
"just works".

And that's actually the reason why I am opposing this kind of change.

As you may know, we are migrating the Qt Creator code base towards a
state where file names are not kept as QStrings but as a separate type,
kind of lightweight URL containing scheme, host and path only. The
structure internally has seen a few iterations now, first three, than
four QStrings, now one QString as common store and three integral
offsets to access the scheme/host/path (sub)strings when needed, as
this means only 8 byte overhead over a plain QString, instead of the
48 bytes for the first shot using trhee full strings.

I am not bound by the same kind of API promises like Qt there, and
since this a quite heavily used structure I am in principle fine
with even "weird" API if it helps e.g. with performance. As a
consequence I actually tried a few ideas to see what helps and what
not /in practice/.

The result is effectively the following:

1. QStringView comes in handy when accessing these substrings.
2. ... but that's effectivly only due to the special nature of
   the storage here.
3. Cutting down such pieces further, comparing etc in the 
   caller feels good, QStringView does the right thing. No copies etc.
4. ... and it doesn't look as ugly as .midRef() etc before.
5. Passing such chunks further on to local helper functions that
   are "implementation details" feels good.
6. That's were it stops.

Most notably, going further, like using QStringView when crossing module
boundaries, feels wrong, even when it technically would work in some
cases.

We have quite a few cases where operation are prepared but executed
delayed, e.g. to not block the gui thread, or not at all, e.g. actions
for context menus that are never triggered by the user. In these cases
the data needs to be copied as the original can vanish.

In other cases the data passes through several layers of truly expensive
but unavoidable operations, so optimizing away one copy is not
noticable. Think for instance what is actually needed when you want to
find out, whether there's an executable "/usr/bin/qmake" on some remote
device.

A similar situation 

Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-10 Thread Thiago Macieira
On Thursday, 10 November 2022 10:40:35 PST Giuseppe D'Angelo via Development 
wrote:
> On 09/11/2022 20:25, Thiago Macieira wrote:
> > Our API should default to passing QStrings for simplicity and not to
> > confuse> 
> > the user. The fact that I cannot wrote:
> >foo(u"bar');
> > 
> > if foo takes a QString is MESSED UP.
> 
> Is this because we deliberately didn't add a QString(const char16_t *)
> constructor, to prevent ambiguities in case we have overloads like these:
> 
>void f(QString)
>void f(QStringView)
> 
> ?

I think so. I understand why we did it. I just think it's wrong.

> (We end up with these overload sets because right now we have tons of
> f(QString), and we're slowly moving them to f(QStringView) if it does
> make sense for them. Due to the BC promise we can't just _remove_
> f(QString).)

And we shouldn't. So we shouldn't be moving to QStringView.

Where it's useful, we can use a Q_WEAK_OVERLOAD. But I insist that we only do 
that where there's a clear performance benefit. Otherwise, keep using QString.

> However: doesn't the new "removed API" system change the status quo? The
> point of the new system is to allow us to preserve BC, while always
> presenting only one of the two functions (the most recent) to client
> code. f(u"foo") would therefore still compile and don't incur in the
> ambiguity.
> 
> (Surely, it's not 100% API compatible, but it would be a SIC A).

It might, indeed.


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



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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-10 Thread Giuseppe D'Angelo via Development

On 09/11/2022 20:25, Thiago Macieira wrote:


Our API should default to passing QStrings for simplicity and not to confuse
the user. The fact that I cannot wrote:

   foo(u"bar');

if foo takes a QString is MESSED UP.


Is this because we deliberately didn't add a QString(const char16_t *) 
constructor, to prevent ambiguities in case we have overloads like these:


  void f(QString)
  void f(QStringView)

?

(We end up with these overload sets because right now we have tons of 
f(QString), and we're slowly moving them to f(QStringView) if it does 
make sense for them. Due to the BC promise we can't just _remove_ 
f(QString).)


However: doesn't the new "removed API" system change the status quo? The 
point of the new system is to allow us to preserve BC, while always 
presenting only one of the two functions (the most recent) to client 
code. f(u"foo") would therefore still compile and don't incur in the 
ambiguity.


(Surely, it's not 100% API compatible, but it would be a SIC A).


Thanks,
--
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: S/MIME Cryptographic Signature
___
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-10 Thread Ivan Solovev via Development
> 1. It doesn't, obviously. If you emit signals, any signal argument must
>be owning, or QueuedConnection cannot be used. Given that C++20
>requires us to mark up views and non-owning ranges (enable_view,
>enable_borrowed_range), I'm confident that we could detect attempts
>to create such connections at compile- or, latest, at run-time, and
>refuse them, or, since we need to serialize the arguments into a
>QMetaCallEvent, anyway, also lifetime-pin, either by holding a weak
>reference to the source object or by storing the data in the event in
>an owning container.

I guess compile-time checks will be quite tricky, because we will need to check 
the threads
of both sender and receiver objects for the Qt::AutoConnection case.
Is it even possible at compile time?

As for the run-time check - I'm not a big fan of this approach either. It will 
be similar to the
metatype check that we have now (and which, I believe, is finally not needed 
with the latest
metatype changes). But I think that the code that compiles and runs, but then 
just refuses
to emit signals, is a potential source of errors and complaints.

> 3. Finally, the kinds of types I'm primarily thinking of in the context
>of NOI are not Q_OBJECTS. It remains to be seen in which form or
>shape co-routine-based reactive programming will complement or even
>substitute signal/slots in the future.

But then we will still have all the Widgets/ItemModels/Netwroking and much more 
around.
And those will still use Qt containers.
So, I would personally agree with what Volker has proposed in his answer:

> We need to come up with a naming convention for getters that returns 
> std::span so that we can add those APIs as alternatives.
> And perhaps we want symmetry between setters and getters working on spans, 
> rather than making std::span setters overloads.

Best regards,
Ivan

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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-10 Thread Marc Mutz via Development
Hi Ivan,

On 10.11.22 11:03, Ivan Solovev via Development wrote:
> I wonder how your NOI-everywhere suggestion will work with the 
> signal/slot connections? Specially for the case when 
> Qt::QueuedConnection is used (explicitly or implicitly).

That's a very good question, thanks.

As a first reaction, three things come to mind:

1. It doesn't, obviously. If you emit signals, any signal argument must
be owning, or QueuedConnection cannot be used. Given that C++20
requires us to mark up views and non-owning ranges (enable_view,
enable_borrowed_range), I'm confident that we could detect attempts
to create such connections at compile- or, latest, at run-time, and
refuse them, or, since we need to serialize the arguments into a
QMetaCallEvent, anyway, also lifetime-pin, either by holding a weak
reference to the source object or by storing the data in the event in
an owning container.

2. QML and QProperty do not appear to require passing the new value in a
NOTIFY signals's parameter, so just not passing the argument is an
option, too (but doesn't work with QueuedConnection, either).

3. Finally, the kinds of types I'm primarily thinking of in the context
of NOI are not Q_OBJECTS. It remains to be seen in which form or
shape co-routine-based reactive programming will complement or even
substitute signal/slots in the future.

So, to summarize, I'd say that NOI works well with signal/slot, too, 
with the exception of QueuedConnection, where we have several options 
for work-arounds.

But this is definitely something we need to keep in mind, going forward, 
indeed.

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] How qAsConst and qExchange lead to qNN

2022-11-10 Thread Ivan Solovev via Development
Hi Marc,

>> I would like to know how that is supposed to work in practice. We have a
>> lot of public API dealing with Qt containers all over. What are you
>> going to do to, for example, to
>>
>>  void addActions(const QList );
>>
>> in qwidget.h? What should it look like when we're done and our users are
>> free to choose the best container for their needs?
>
> In this particular case, the API would be
>
> void addActions(QSpan actions);
>
> This is the trivial case: setters for contiguous data. It's
> backwards-compatible, because QList implicitly converts to QSpan (and
> std::span), but now, as a user, I can also pass a C array or std::vector
> or QVLA of QAction*

I wonder how your NOI-everywhere suggestion will work with the signal/slot 
connections? Specially for the case when Qt::QueuedConnection is used 
(explicitly or implicitly).

Best regards,
Ivan

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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-09 Thread Kevin Kofler via Development
A. Pönitz wrote:
> [And since we are at it: It would ease my life (but possibly hamper the
> life of my cardiologist) if you wouldn't call it "Qt NIH API". This is
> an open insult to people who actively designed these APIs _with
> different goals_ than the STL. I personally think it's completely fine
> if you don't agree with these different goals, but then please accept
> that these goals exist, too]

Not to mention that some of the Qt APIs actually predate their STL 
equivalent, making the STL version the NIH one.

Kevin Kofler

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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-09 Thread Thiago Macieira
On Wednesday, 9 November 2022 11:25:32 PST Thiago Macieira wrote:
> On Wednesday, 9 November 2022 02:52:15 PST Marc Mutz via Development wrote:
> > We can also return spans:
> > QSpan actions() const;
> 
> we CANNOT and MUST NOT.

Unless you can find a way to return the collection  of QActions from:

QMap actions;

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



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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-09 Thread Thiago Macieira
On Wednesday, 9 November 2022 02:52:15 PST Marc Mutz via Development wrote:
> This is the same effect we have with Q*StringView, incl. avoidance of
> caller-side construction and destruction of owning containers.

Indeed, which is WRONG.

Our API should default to passing QStrings for simplicity and not to confuse 
the user. The fact that I cannot wrote:

  foo(u"bar');

if foo takes a QString is MESSED UP.

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



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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-09 Thread Thiago Macieira
On Wednesday, 9 November 2022 02:52:15 PST Marc Mutz via Development wrote:
> We can also return spans:
> 
> QSpan actions() const;

we CANNOT and MUST NOT.

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



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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-09 Thread Ulf Hermann via Development

Hi,

let me mention that I've been pondering all that hairy stuff just 
recently when trying to come up with a solution for how to map QList*> and QQmlListProperty to each other without exposing people to 
dangling references and without copying and allocating all the time.


One thing we could do is add a special escape hatch to QSpan (or 
QQmlListProperty) that makes it easy to retrieve the underlying QList in 
the cases where it's just a thin wrapper around a complete QList. That 
thing could then be used to implicitly construct a (shared) QList from a 
QSpan.


With this, you'd get the old behavior if you just pass QList around. 
Your QLists are just wrapped and unwrapped in QSpans when passing Qt's 
API boundary. However, you could also pass other things that can be 
expressed as QSpan, and new Qt APIs could return QSpans that are not 
backed by QLists.


I realize that this proposal has very little to do with std::span. It 
also doesn't solve all of my problems with QQmlListProperty, but it 
might be part of a solution.


best regards,
Ulf

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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-09 Thread Volker Hilsheimer via Development


> On 9 Nov 2022, at 12:49, Marc Mutz via Development 
>  wrote:
> 
> Hi Volker,
> 
> On 09.11.22 10:15, Volker Hilsheimer wrote:
>> But I do believe that we can add APIs that are iterator and ranges friendly 
>> to Qt without tossing out the baby with the bathwater and without breaking 
>> tons of code and patterns. For APIs where we simply return a stored QList we 
>> want to be able to return that QList as a shared copy. If we would only 
>> return a std::span as a view on the stored list, then client code will need 
>> to construct their copy from that std::span. That is equally silly.
> 
> There are two assumptions you are making here, and it's important to 
> understand them when discussing NOI:
> 
> First, that when we return a copy of a stored QList, most users would 
> keep the data around for longer.
> 
> I don't have the stats to prove it, but I'd say that that's a corner 
> case, not the general case. Even when talking about storing the data 
> temporarily in a function, no owning container is required. And if 
> there's no Qt API that requires you to pass owning containers, passing 
> from and to Qt API also drops out of the use-cases that require 
> returning a shared copy of a QList.


The assumption that I don’t have to make is: there is a tone of working code 
that calls existing Qt APIs to get or set QList values on objects. That code 
might or might not assume that this operation is cheap as far as making a 
shared copy of that list is concerned. The API called might or might not take 
that list and store it, rather than process it and store it in some other form. 
I honestly don’t care. I know that the code exists and does what the people who 
wrote it expect it to do.

We can probably in most cases add overloads to setters that take a 
std::span/QSpan, and construct whatever the implementation needs to store from 
that. But we cannot remove existing QList-taking setters, and we cannot change 
existing getters that return a QList to return a std::span. Even if it’s 
API/ABI compatible with the help from QT_REMOVED_SINCE.


> Second, that the data is stored in a QList in the first place.


Yes, that’s the case that I explicit mentioning in my email. It would be great 
to have alternatives that can return a std::span, and that doesn’t need to 
construct a QList or whatever in the first place.

An alternative, not a replacement. I am making assumptions, you are wagering. 
We are not going to break

if (widgets->actions().contains(myAction)) ...

based on wagers and assumptions.

We need to come up with a naming convention for getters that returns std::span 
so that we can add those APIs as alternatives. And perhaps we want symmetry 
between setters and getters working on spans, rather than making std::span 
setters overloads.


Cheers,
Volker

SBO: short buffer optimization
NOI…? Non-owning … interface?

Your arguments would be easier to follow with fewer acronyms ;)


> Even most Qt types don't have a need for CoW semantics of QList in their 
> implementation. The perceived need is coming solely from having to hand 
> out owning containers in the API, an operation we don't want to 
> deep-copy. So, catch-22.
> 
> But imagine you /don't/ need to hand out owning containers from your 
> API, what container would you choose, then? Well, QRegion chose 
> variant>, and I've repeatedly shown how that 
> caused users to have to use rectCount() and boundingRect() to avoid the 
> conversion of the first into the latter option.
> 
> QVersionNumber uses SBO to store short version numbers inline, only 
> falling back to QList if the number isn't representable as a qint8[3]. 
> The implementation is truly horribly complex, and the API is more 
> limited (the user can't iterate over the segments) than it would be if 
> it had not used SBO.
> 
> I'd wager that a lot more Qt types would rather benefit from a non-CoW 
> SBO container than benefit from the non-SBO CoW ones we currently have.
> And the use of QList in the  API is holding those types back from using the
> optimal data structure,  because QList is under compatibility constraints, so
> even where we _have_ an SBO container (e.g. std::string as a stand-in for
> QByteArray  or std::u16string for QString), we can't use it, because the API 
> uses 
> owning Qt containers and we can't change them. Or we do, and then we 
> continue the vicious cycle of Qt container churn on each major release.
> 
> NOI can break that cycle, so the Qt containers don't need to change, 
> keeping existing users happy, and both the implementations and users of 
> our API are free to choose whatever data structure best fits their 
> needs, making Qt developers and new users happy.
> 
> Win-win.
> 
> 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 

Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-09 Thread Marc Mutz via Development
Hi Volker,

On 09.11.22 10:15, Volker Hilsheimer wrote:
> But I do believe that we can add APIs that are iterator and ranges friendly 
> to Qt without tossing out the baby with the bathwater and without breaking 
> tons of code and patterns. For APIs where we simply return a stored QList we 
> want to be able to return that QList as a shared copy. If we would only 
> return a std::span as a view on the stored list, then client code will need 
> to construct their copy from that std::span. That is equally silly.

There are two assumptions you are making here, and it's important to 
understand them when discussing NOI:

First, that when we return a copy of a stored QList, most users would 
keep the data around for longer.

I don't have the stats to prove it, but I'd say that that's a corner 
case, not the general case. Even when talking about storing the data 
temporarily in a function, no owning container is required. And if 
there's no Qt API that requires you to pass owning containers, passing 
from and to Qt API also drops out of the use-cases that require 
returning a shared copy of a QList.

Second, that the data is stored in a QList in the first place.

Even most Qt types don't have a need for CoW semantics of QList in their 
implementation. The perceived need is coming solely from having to hand 
out owning containers in the API, an operation we don't want to 
deep-copy. So, catch-22.

But imagine you /don't/ need to hand out owning containers from your 
API, what container would you choose, then? Well, QRegion chose 
variant>, and I've repeatedly shown how that 
caused users to have to use rectCount() and boundingRect() to avoid the 
conversion of the first into the latter option.

QVersionNumber uses SBO to store short version numbers inline, only 
falling back to QList if the number isn't representable as a qint8[3]. 
The implementation is truly horribly complex, and the API is more 
limited (the user can't iterate over the segments) than it would be if 
it had not used SBO.

I'd wager that a lot more Qt types would rather benefit from a non-CoW 
SBO container than benefit from the non-SBO CoW ones we currently have.
And the use of QList in the  API is holding those types back from using the
optimal data structure,  because QList is under compatibility constraints, so
even where we _have_ an SBO container (e.g. std::string as a stand-in for
QByteArray  or std::u16string for QString), we can't use it, because the API 
uses 
owning Qt containers and we can't change them. Or we do, and then we 
continue the vicious cycle of Qt container churn on each major release.

NOI can break that cycle, so the Qt containers don't need to change, 
keeping existing users happy, and both the implementations and users of 
our API are free to choose whatever data structure best fits their 
needs, making Qt developers and new users happy.

Win-win.

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] How qAsConst and qExchange lead to qNN

2022-11-09 Thread Marc Mutz via Development
Hi Ulf,

On 09.11.22 08:18, Ulf Hermann via Development wrote:
>> I don't want to take the Qt containers away from Qt users. I want to get
>> rid of their use in our APIs, so that both the Qt implementation as well
>> as our users are free to choose the best container for their needs
>> instead of having to pick from one of the public Qt containers.
> 
> I would like to know how that is supposed to work in practice. We have a 
> lot of public API dealing with Qt containers all over. What are you 
> going to do to, for example, to
> 
>  void addActions(const QList );
> 
> in qwidget.h? What should it look like when we're done and our users are 
> free to choose the best container for their needs?

In this particular case, the API would be

void addActions(QSpan actions);

This is the trivial case: setters for contiguous data. It's 
backwards-compatible, because QList implicitly converts to QSpan (and 
std::span), but now, as a user, I can also pass a C array or std::vector 
or QVLA of QAction*:

QAction *actions[] = {
   new FooAction(this),
   new BarAction(this),
};
addActions(actions);

This is the same effect we have with Q*StringView, incl. avoidance of 
caller-side construction and destruction of owning containers.

This is NOIv1: https://www.youtube.com/watch?v=JUUID0_NvQI

We can also return spans:

QSpan actions() const;

but this only works, though, if we're returning a) stored and b) 
contiguous data. If we return computed or non-contiguous data, instead, 
we can, however, use coroutines:

   std::g/QGenerator selectedIndexes() const;

This is NOIv2: https://www.youtube.com/watch?v=tvdwYwTyrig

The benefit here isn't only that the implementation data structure is 
encapsulated, but also that, for computed values, the computation stops 
when the consumer stops consuming.

The drawback is that the memory allocation of the coroutines' stack 
frame is not elidable (crosses ABI boundaries), but when compared to 
returning computed owning containers, you don't have more, and typically 
have much less, memory allocations.

> Mind that I'm specially interested in this because I'm currently facing 
> a similar problem, making different container types available in QML. 
> QML has its own poor man's "range" type in the form of QQmlListProperty 
> and it's terrible.

I don't know the QML landscape nearly as well as I should, but I would 
invite you to consider the role coroutines can play in implementing 
reactive programming environments such as QML and QProperty, even if we 
cannot rely on them atm.

>>   >> Q_FOREACH
>>   > [I can make 100% correct predictions about changes I intent to push,
>>   > too. What's the point?]
>>
>> I have no desire to touch the implementation of Q_FOREACH, ever. I did,
>> unwillingly, when its users suffered unnecessary pessimisations in the
>> past, but the port to C++20 ranged-for-with-init is not of that kind.
> 
> Waiting for someone to push a patch with code you've already outlined 
> and then approving it is pretty much the same as changing it yourself. 
> You just don't need any approval for that ...

I can honestly say that I was surprised about the effect of the C++20 
feature on the Q_FOREACH implementation. I was just predicting by
extrapolating from  the last change to Q_FOREACH (using C++17
if-with-init), and wasn't  aware of the consequences until after writing
down the implementation :)

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] How qAsConst and qExchange lead to qNN

2022-11-09 Thread Philippe
> Volker Hilsheimer via Development  wrote:

nice pragmatic overview :)

Philippe

On Wed, 9 Nov 2022 09:15:16 +
Volker Hilsheimer via Development  wrote:

> Hi,
> 
> > On 8 Nov 2022, at 22:20, Marc Mutz via Development 
> >  wrote:
> > To summarize:
> > - I will not accept responsibility for any container rewrites in any of
> >   the Qt major version changes. I was not involved in any of these
> >   decisions, and where I was involved in the discussion, my suggestions
> >   were not followed.
> 
> 
> Indeed. And those who were involved, and made those decisions, did so with 
> best intentions.
> 
> Let’s please keep a respectful tone in this conversation. Finger-pointing is 
> not going to get us anywhere, and I hope we can learn and come up with good 
> steps forward without making this personal.
> 
> 
> > - I do not want to take Qt containers away from Qt users. Instead, I
> >   want our APIs to stop forcing our users (and us) to use (owning) Qt
> >   containers.
> 
> 
> It would be great if we can add mechanisms in Qt that allows allows people to 
> iterate e.g. over the selected rows in a QItemSelectionModel, without Qt 
> having to create a temporary list with a ton of memory allocation, just so 
> that the calling code can throw the list away after iterating over it once. 
> There are many APIs in Qt where the returned value is going to be temporary. 
> QObject::findChildren is another example - we iterate over objects to create 
> the list, then we return the list, then client code iterates over the list to 
> do something with the objects (perhaps only the first).
> 
> That’s clearly inefficient, and it impacts Qt in many places and modules. 
> Making a e.g. std::span available with appropriately implemented iterators 
> would help with that, with the possible foot-gun that people that keep that 
> std::span around will end up with invalidated iterators.
> 
> But I do believe that we can add APIs that are iterator and ranges friendly 
> to Qt without tossing out the baby with the bathwater and without breaking 
> tons of code and patterns. For APIs where we simply return a stored QList we 
> want to be able to return that QList as a shared copy. If we would only 
> return a std::span as a view on the stored list, then client code will need 
> to construct their copy from that std::span. That is equally silly.
> 
> I really think that we can add many of those new capabilities without causing 
> unnecessary churn, and without introducing incompatibilities or deprecations. 
> And in the few cases where we can’t without seriously compromising the 
> end-goal or quality of the overall API, a public discussion here on the 
> mailing list is necessary.
> 
> 
> Volker
> 
> ___
> Development mailing list
> Development@qt-project.org
> https://lists.qt-project.org/listinfo/development


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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-09 Thread Volker Hilsheimer via Development
Hi,

> On 8 Nov 2022, at 22:20, Marc Mutz via Development 
>  wrote:
> To summarize:
> - I will not accept responsibility for any container rewrites in any of
>   the Qt major version changes. I was not involved in any of these
>   decisions, and where I was involved in the discussion, my suggestions
>   were not followed.


Indeed. And those who were involved, and made those decisions, did so with best 
intentions.

Let’s please keep a respectful tone in this conversation. Finger-pointing is 
not going to get us anywhere, and I hope we can learn and come up with good 
steps forward without making this personal.


> - I do not want to take Qt containers away from Qt users. Instead, I
>   want our APIs to stop forcing our users (and us) to use (owning) Qt
>   containers.


It would be great if we can add mechanisms in Qt that allows allows people to 
iterate e.g. over the selected rows in a QItemSelectionModel, without Qt having 
to create a temporary list with a ton of memory allocation, just so that the 
calling code can throw the list away after iterating over it once. There are 
many APIs in Qt where the returned value is going to be temporary. 
QObject::findChildren is another example - we iterate over objects to create 
the list, then we return the list, then client code iterates over the list to 
do something with the objects (perhaps only the first).

That’s clearly inefficient, and it impacts Qt in many places and modules. 
Making a e.g. std::span available with appropriately implemented iterators 
would help with that, with the possible foot-gun that people that keep that 
std::span around will end up with invalidated iterators.

But I do believe that we can add APIs that are iterator and ranges friendly to 
Qt without tossing out the baby with the bathwater and without breaking tons of 
code and patterns. For APIs where we simply return a stored QList we want to be 
able to return that QList as a shared copy. If we would only return a std::span 
as a view on the stored list, then client code will need to construct their 
copy from that std::span. That is equally silly.

I really think that we can add many of those new capabilities without causing 
unnecessary churn, and without introducing incompatibilities or deprecations. 
And in the few cases where we can’t without seriously compromising the end-goal 
or quality of the overall API, a public discussion here on the mailing list is 
necessary.


Volker

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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-08 Thread Ulf Hermann via Development

I don't want to take the Qt containers away from Qt users. I want to get
rid of their use in our APIs, so that both the Qt implementation as well
as our users are free to choose the best container for their needs
instead of having to pick from one of the public Qt containers.


I would like to know how that is supposed to work in practice. We have a 
lot of public API dealing with Qt containers all over. What are you 
going to do to, for example, to


void addActions(const QList );

in qwidget.h? What should it look like when we're done and our users are 
free to choose the best container for their needs?


Mind that I'm specially interested in this because I'm currently facing 
a similar problem, making different container types available in QML. 
QML has its own poor man's "range" type in the form of QQmlListProperty 
and it's terrible.



  >> Q_FOREACH
  > [I can make 100% correct predictions about changes I intent to push,
  > too. What's the point?]

I have no desire to touch the implementation of Q_FOREACH, ever. I did,
unwillingly, when its users suffered unnecessary pessimisations in the
past, but the port to C++20 ranged-for-with-init is not of that kind.


Waiting for someone to push a patch with code you've already outlined 
and then approving it is pretty much the same as changing it yourself. 
You just don't need any approval for that ...


best regards,
Ulf
___
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-08 Thread Marc Mutz via Development
Hi Andre,

When you say

 > You, /personally/ /you/, have been the driving force

I think you and Kevin are giving me credit where none is due. The Qt 
containers changed multiple times before the Qt project was even formed 
and yours truly could have possibly had any influence on their design.

Interestingly, when you say

 > Staying with unmodified Qt 4 containers would very likely just have
 > worked in Qt 6 and not have caused any problems for users upgrading

Two things spring to mind:

First, that it's probably not a coincidence that stability came right 
after Qt adopted basically the STL model. QVector was std::vector + 
COW, QMap was std::map + COW, and the old QGList experiment was finally 
put to rest as Q3PtrList.

Second, that I completely agree with you. I think the decision to make 
Q6List be Q5Vector and add a prepend optimisation was a major mistake. 
We should have told people to prepare for QList -> QVector by using auto 
when receiving QLists from Qt, added implicit conversions between them
for when users were passing in QLists, and then we should have cleaned _our_ 
basement by porting away from QList to QVector (we anyway did that), but 
leave Q5List as-is in qt5compat. I think you will find me and Peppe on 
record on this mailing list in the run-up to Qt 6 asking exactly that. 
Alas, TPTB decided, instead, to

- reuse the QList name for QVector
- _not_ provide Q5List in qt5compat

thus breaking not only our own uses (which we had to do, anyway, in the 
move from QList to QVector), but also our users' code. Silently, I 
hasten to add.

Finally, when you say

 > I haven't checked
 > explicitly, but my best guess is that Qt 4.0 (2005(!)) would
 > container-wise be "good enough" for my current needs.

We're in violent agreement, too. Why take them from someone for whom the 
Qt4 containers are good enough™? From my pov, the Qt containers can be 
moved into a libQtQTL or something like that and enjoy their life as a 
slightly different approach to the STL containers. Then users that don't 
need Qt per-se could opt-in to using the Qt containers _only_ (and a 
similar argument can be made for the Qt string types; libQt7Strings; 
CopperSpice says to say hello), potentially drawing in more users, if 
they, indeed, serve a different audience than the STL ones.

What I _do_ care about is that Qt atm doesn't let the user /choose/. 
Because of their ubiquitous use in Qt APIs, the user is forced to use 
the Qt containers, even though he may have a need for the STL containers 
(interop with 3rd-party libraries, allocators, > 2Gi elements, etc).

I don't want to take the Qt containers away from Qt users. I want to get 
rid of their use in our APIs, so that both the Qt implementation as well 
as our users are free to choose the best container for their needs 
instead of having to pick from one of the public Qt containers.

Let each user choose among the Qt, STL, Boost, Abseil, and Folly 
container classes on a level playing field.

Now, if you like an intellectual challenge, the please explain to me how 
Qt's insert-or-assign is either more convenient, easier, more 
user-friendly, more idiomatic, or whatever, than STL's insert-or-no-op 
If, as you say, the Qt containers are carefully designed to a different 
set of goals, then I'd really like to know what the goal looks like that 
made Qt choose this design.

To summarize:
- I will not accept responsibility for any container rewrites in any of
   the Qt major version changes. I was not involved in any of these
   decisions, and where I was involved in the discussion, my suggestions
   were not followed.
- I do not want to take Qt containers away from Qt users. Instead, I
   want our APIs to stop forcing our users (and us) to use (owning) Qt
   containers.

Finally,

 >> Q_FOREACH
 > [I can make 100% correct predictions about changes I intent to push,
 > too. What's the point?]

I have no desire to touch the implementation of Q_FOREACH, ever. I did, 
unwillingly, when its users suffered unnecessary pessimisations in the 
past, but the port to C++20 ranged-for-with-init is not of that kind.

 > string UDLs

 From my POV, we were fixing up a prior experiment-gone-wrong. I laid 
out the problems of the _qs UDL design in 
https://lists.qt-project.org/pipermail/development/2022-March/042335.html, 
so I won't repeat them here.

Thanks,
Marc

On 08.11.22 20:32, A. Pönitz wrote:
> On Mon, Nov 07, 2022 at 08:15:58PM +, Marc Mutz via Development wrote:
>> Hi all,
>>
>> SCNR taking the proffered bait:
>>
>> On 07.11.22 18:51, Edward Welbourne wrote:
>>> For Qt to remain relevant in the rapidly-evolving C++ landscape, it
>>> needs to change; but one of its strong selling-points has long been its
>>> conservatism in API and ABI, that lets working code keep working.
>>
>> I've not been around for the Qt 1 → Qt 2 port, but I did participate in
>> all of the Qt 2 → Qt 3, Qt 3 → Qt 4, Qt 4 → Qt 5 and Qt 5 → Qt 6
>> transitions. Speaking with my Qt user hat on, I'm 

Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-08 Thread A . Pönitz
On Mon, Nov 07, 2022 at 08:15:58PM +, Marc Mutz via Development wrote:
> Hi all,
> 
> SCNR taking the proffered bait:
> 
> On 07.11.22 18:51, Edward Welbourne wrote:
> > For Qt to remain relevant in the rapidly-evolving C++ landscape, it
> > needs to change; but one of its strong selling-points has long been its
> > conservatism in API and ABI, that lets working code keep working.
> 
> I've not been around for the Qt 1 → Qt 2 port, but I did participate in 
> all of the Qt 2 → Qt 3, Qt 3 → Qt 4, Qt 4 → Qt 5 and Qt 5 → Qt 6 
> transitions. Speaking with my Qt user hat on, I'm terribly sorry to 
> inform y'all that Qt has largely _failed_ to keep working code working. 
> Sure, a trivial QWidgets program from the mid-90s may still compile and 
> work in Qt 6, but as soon as said program touches Qt container classes, 
> it's game over.

Staying wiht unmodified Qt 4 containers would very likely just have
worked in Qt 6 and not have caused any problems for users upgrading
their applications during the last 15 years.
 
> Both Boost and C++ itself have a much better track record of keeping 
> working code working, let alone any random C library. And this is 
> actually a fair comparison, because we're comparing apples (STL 
> containers) to apples (Qt containers).

No. Goals of STL and Qt containers are /quite/ different.

Qt offers convenience and ease of use while still being "good enough"
at performance for normal use. STL can give better performance in some
cases, but needs more handholding, more alertness from the user, and
it's easier to get things completely wrong, also performance-wise.

This is absolutely not "apples vs apples", rather (tr("Nicht alles, was
hinkt, ist ein Vergleich, aber:")) "cars vs motorcycles": Sure, they have
a lot in common, but none is uniformly "better" than the other.

> Let's not kid ourselves thinking Qt is any special here. It isn't. We 
> mustn't just look at one major release cycle. Qt loses projects at every 
> new major release, because changes to non-core-competency parts of the 
> API make porting unnecessarily complicated, so that unmaintained or 
> understaffed projects[1] cannot muster the strength to be ported.

I consider this an absurd line of reasoning coming from /you/.

You, /personally/ /you/, have been the driving force behind a
significant amount of /from my perspective/ unnecessary and absolutely
unwelcomed changes like the removal of Q(5)List that I predicted to be
and now apparently _is_ hampering "understaffed projects" (which in my
book covers pretty much /every/ open source projects and quite a few
commercial ones) to move from Qt 5 to Qt 6.

This "loss of projects" is not the consequence of any natural law or
divine intervention. Even "understaffed projects" can typically "muster
the strength" to do _necessary_ adaptations to _critical_ problems.
However, it is completely no surprise _to me_ that they do not want to
jump through any hoop that is effectively only busy-work, not solving
any real problem.

> My goal with qNN is to make porting away _simple_. All that's required 
> is to s/qNN::/std::/ and be done. No deprecation, no sanity bot, not 
> even the need for local code knowledge; just simple global textual 
> replacement. And no regressions, if you first switch to C++NN, and only 
> then do the replacement. We can even retain the qNN symbols until Qt 
> requires C++(NN+3), because the qNN headers will be nothing but a long 
> list of using std::foo; at that time.
> 
> It's not really acceptable that such trivial ports should be subjected 
> to all the same (or, apparently since it's done in bulk, more 
> restrictive) requirements than for deprecation of core-competency APIs. 
> The more so as I must have missed the outcry of developers when we inflicted

_To me_ it is not really acceptable that (parts of) qtbase are treated
as playground for experiments. [And _I_ _do_ think that e.g.  calling
"string literal qualifications du jour" that barely last for a year "an
experiment" is fair].

In my world, deprecations ("trivial" or not) _in a library_ should only
happen as a consequence of an unexpected _and_ fundamental _and_ otherwise
unfixable flaws in the API, causing _real_ (not "imagined") harm for "a
lot" of users. This should never be used wantonly, and definitely not
pre-planned.

> To hold users, a project must maintain _long-term_ API stability, not 
> rewrite the container classes for every major release.

... g ... 

> So, sorry, but as a user of Qt I'd really like to use those stable STL 
> classes, if only your volatile APIs let me. I'd rather I had done that 
> _one_ port between Qt 1 and 2 than all those ports in Qt 1-6.

Again, /you/, /personally/ /you/, have been /actively/ involved and
promoted and/or done part of these /breaking/ changes. I haven't checked
explicitly, but my best guess is that Qt 4.0 (2005(!)) would
container-wise be "good enough" for my current needs. And it's not that
STL has't changed since then...


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-08 Thread Alex Blasche via Development


--
Alex

> -Original Message-
> From: Development  On Behalf Of
> Volker Hilsheimer via Development
> Sent: Monday, 7 November 2022 16:51
> To: Marc Mutz ; development@qt-project.org
> Subject: Re: [Development] How qAsConst and qExchange lead to qNN
> 
> > On 4 Nov 2022, at 16:00, Marc Mutz via Development  project.org> wrote:
> >
> > Hi,
> >
> > After getting my head washed by Volker, lemme provide background on
> > these two functions.
> 
> Thanks for the context, Marc!
> 
> > TL;DR: we created real maintenance and porting problems by not
> > removing stop-gap functionality in a timely fashion, qNN presented as
> > a way to ensure this won't happen again.
> >
> > Both qAsConst and qExchange are 1:1 reimplementations of std
> > functionality we couldn't rely on back then (as_const is C++17 and
> > exchange is C++14), so they're exactly equivalent to the std versions.
> > Or were, when they were added.
> >
> > Neither std::as_const nor qAsConst have changed, so the replacement is
> > easy: s/qAsConst/std::as_const/. It cannot not compile because
> > qAsConst comes from qglobal.h and that transitively includes
> > . This is what was intended from the get-go:
> 
> 
> The open question is whether and when we should deprecate such a stop-gap
> 1:1 reimplementations of std functionality. How to deprecate is now well
> documented, but the wiki starts with the process of doing so once we concluded
> that we shall. It doesn’t give us any guidance yet on how to come to that
> conclusion.

I had to ask Volker which wiki he refers to and maybe it is the same for 
somebody else too. The URL is 

https://wiki.qt.io/Deprecation

> When it’s time to phase out one of our own qNN implementations, then
> 
> 1) propose the change here first to raise awareness, and to give people time 
> to
> ask questions and/or raise objections
> 
> Even if the people doing the work all agree, a lot of maintainers and
> contributors will still be impacted (at least by the tool being removed). The
> proposal should come with some data about how prevalent the usage of the
> relevant construct is in Qt. It makes a difference whether we’d have to touch 
> a
> few dozen lines, or several hundred to remove all usage.
> 
> 2) If possible, add a warning to the sanity bot so that no new usage is added
> 
> This is trivial in some cases, not so trivial in others. Rationale: For 
> changes that
> impact a larger amount of code, there’ll be plenty of time between those
> changes getting merged, and the old Qt-implementation ultimately getting
> removed or fully deprecated (which we can’t/shouldn’t do while we still have
> usage in Qt itself). For example, we now have some qAsConst back in the qtbase
> code.

I support this. In particular, the open communication before the fact is the 
key here. Let's face it, Qt is large enough that it cannot be expected that 
everybody knows what's going on in all the modules and such changes may never 
hit a developer's radar until after the merge and its enforcement.

I propose we add the gist of Volker's proposal to the deprecation wiki 
mentioned above.

> Whether we then do a bulk replacement in Qt, or whether we just stop using old
> stuff in new code and phase it out over time as we touch code (until here’s
> perhaps little enough left to make a bulk change), depends on the discussion. 
> If
> we do make a bulk change, then making that change in stable branches to avoid
> cherry-picking conflicts would probably be ok as well (unless those branch 
> can’t
> use the new C++ version yet).

Though Volker kind of implies that his two rules might lead to cherry-picking 
back into older releases, I would like to see this as explicitly mentioned 
option wherever we document the rules.

> From: Development  On Behalf Of
> Marc Mutz via Development

>#define Q_FOREACH(decl, expr) \
>   for (const auto _q_foreach_c = expr; decl : _q_foreach_c)
> 
> And I'd probably approve it, because then that thing can actually just be 
> replaced
> with the expansion everywhere, and then, finally, be deleted.

Considering the above, this kind of change would have to be brought up to the 
mailing list and be discussed before any approval is given. 

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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-07 Thread Kevin Kofler via Development
Marc Mutz via Development wrote:
> Sure, a trivial QWidgets program from the mid-90s may still compile and
> work in Qt 6, but as soon as said program touches Qt container classes,
> it's game over.

A lot of the changes to Qt container classes were either driven by you or at 
least praised by you, so it is really unfair for you to now blame Qt for 
those changes. Those changes were mainly driven by:
* raw performance over backwards compatibility, and sometimes even over API 
convenience, and
* interoperability with new C++ and/or STL features such as std::move,
both of which are causes that you have been heavily championing for all this 
time.

Qt could have easily kept old programs just working unmodified. It is you 
and other "modern C++" promoters that have forced it to break compatibility 
at everyone else's expense.

> Both Boost and C++ itself have a much better track record of keeping
> working code working, let alone any random C library. And this is
> actually a fair comparison, because we're comparing apples (STL
> containers) to apples (Qt containers).

The answer to that is that Qt just needs to stop doing incompatible changes. 
It is time to consider the Qt containers done and retain API and ABI 
compatibility forever. IMHO, that could and should have happened even with 
the Qt 4 version.

> Let's not kid ourselves thinking Qt is any special here. It isn't. We
> mustn't just look at one major release cycle. Qt loses projects at every
> new major release, because changes to non-core-competency parts of the
> API make porting unnecessarily complicated, so that unmaintained or
> understaffed projects[1] cannot muster the strength to be ported.

See above. Qt should not be doing major (i.e., API/ABI-incompatible) 
releases at all anymore. (Heck, you can bump the first digit if you add some 
great new feature, without necessarily breaking compatibility.)

> My goal with qNN is to make porting away _simple_. All that's required
> is to s/qNN::/std::/ and be done. No deprecation, no sanity bot, not
> even the need for local code knowledge; just simple global textual
> replacement. And no regressions, if you first switch to C++NN, and only
> then do the replacement. We can even retain the qNN symbols until Qt
> requires C++(NN+3), because the qNN headers will be nothing but a long
> list of using std::foo; at that time.

How about #define q17 std? Or even #define qAsConst std::as_const? If it is 
really a drop-in replacement as intended, that should work. (And in the 
unlikely event of a name conflict with user code, #undef can be used as a 
quick workaround.)

> It's not really acceptable that such trivial ports should be subjected
> to all the same (or, apparently since it's done in bulk, more
> restrictive) requirements than for deprecation of core-competency APIs.
> The more so as I must have missed the outcry of developers when we
> inflicted
> 
>// Universe A
>Qt 1  Qt 2 Qt 3 Qt 4+5 Qt 6
>QGList -> QList -> QPtrList / QValueList -> QList / QVector -> QList
>QLinkedList

Oh, I did complain about the QList changes in Qt 6. You, on the other hand, 
have proposed (on 2017-03-18) to just "Kill QList in Qt 6" which would have 
broken existing application code even more than the change that was 
implemented.

And I was not yet on this list when the Qt 4 changes happened, or I would 
have complained about those, too.

> on an audience that could, instead, have had
> 
>// Universe B
>Qt 1  Qt 2-4 Qt 4-5 Qt 6   Qt 6-7
>QGList -> std::vector -> std::vector -> std::vector -> std::vector
>CfrontC++98  C++11/14   C++17  C++20/23

But std::vector is much worse than any of the Qt containers above: no 
implicit sharing, uglier and less complete API, no amortized O(1) prepending 
optimization.

The Qt 5 QList also had efficient random insertions/removals for large 
structures, a feature that was actually LOST in the Qt 6 version. (Yes, you 
can explicitly store pointers in the list to simulate the old behavior, but 
that means less convenient API.) But std::vector never had that feature to 
begin with.

> To hold users, a project must maintain _long-term_ API stability, not
> rewrite the container classes for every major release.

Indeed. So why have you not supported keeping the Qt 5 QList in Qt 6 then?

> So, sorry, but as a user of Qt I'd really like to use those stable STL
> classes, if only your volatile APIs let me. I'd rather I had done that
> _one_ port between Qt 1 and 2 than all those ports in Qt 1-6.

I would rather have kept the Qt containers without constant porting and 
without losing features (which the STL never had to begin with).

Did you know that Qt 3 containers actually had defined behavior on 
overflows? (An out-of-bounds read would always return 0, which was 
especially useful for strings.) That was dropped in Qt 4 in the 

Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-07 Thread Marc Mutz via Development
Hi all,

SCNR taking the proffered bait:

On 07.11.22 18:51, Edward Welbourne wrote:
> For Qt to remain relevant in the rapidly-evolving C++ landscape, it
> needs to change; but one of its strong selling-points has long been its
> conservatism in API and ABI, that lets working code keep working.

I've not been around for the Qt 1 → Qt 2 port, but I did participate in 
all of the Qt 2 → Qt 3, Qt 3 → Qt 4, Qt 4 → Qt 5 and Qt 5 → Qt 6 
transitions. Speaking with my Qt user hat on, I'm terribly sorry to 
inform y'all that Qt has largely _failed_ to keep working code working. 
Sure, a trivial QWidgets program from the mid-90s may still compile and 
work in Qt 6, but as soon as said program touches Qt container classes, 
it's game over.

Both Boost and C++ itself have a much better track record of keeping 
working code working, let alone any random C library. And this is 
actually a fair comparison, because we're comparing apples (STL 
containers) to apples (Qt containers).

Let's not kid ourselves thinking Qt is any special here. It isn't. We 
mustn't just look at one major release cycle. Qt loses projects at every 
new major release, because changes to non-core-competency parts of the 
API make porting unnecessarily complicated, so that unmaintained or 
understaffed projects[1] cannot muster the strength to be ported.

My goal with qNN is to make porting away _simple_. All that's required 
is to s/qNN::/std::/ and be done. No deprecation, no sanity bot, not 
even the need for local code knowledge; just simple global textual 
replacement. And no regressions, if you first switch to C++NN, and only 
then do the replacement. We can even retain the qNN symbols until Qt 
requires C++(NN+3), because the qNN headers will be nothing but a long 
list of using std::foo; at that time.

It's not really acceptable that such trivial ports should be subjected 
to all the same (or, apparently since it's done in bulk, more 
restrictive) requirements than for deprecation of core-competency APIs. 
The more so as I must have missed the outcry of developers when we inflicted

   // Universe A
   Qt 1  Qt 2 Qt 3 Qt 4+5 Qt 6
   QGList -> QList -> QPtrList / QValueList -> QList / QVector -> QList
   QLinkedList

on an audience that could, instead, have had

   // Universe B
   Qt 1  Qt 2-4 Qt 4-5 Qt 6   Qt 6-7
   QGList -> std::vector -> std::vector -> std::vector -> std::vector
   CfrontC++98  C++11/14   C++17  C++20/23

To hold users, a project must maintain _long-term_ API stability, not 
rewrite the container classes for every major release.

So, sorry, but as a user of Qt I'd really like to use those stable STL 
classes, if only your volatile APIs let me. I'd rather I had done that 
_one_ port between Qt 1 and 2 than all those ports in Qt 1-6.

Well, as they say, if 20 years ago was the best time to do the switch, 
then the next-best time is today[2].

Anyway; to all those who disagree when I say Qt should concentrate on 
its core competencies and stop meddling with container classes, shared 
pointers, etc, I say this: which of the two universes above would you 
rather have lived in these past 30 years? A or B? Be truthful!

Because, you see, whenever we phase out some Qt NIH API, we beam a small 
part of your (and our!) code from Universe A into the Universe B. I 
_really_ can't understand why anyone would complain. Must be a case of 
recency bias or something like that...

Speaking of the devil, from my pov, qAsConst isn't "small enough" to 
survive. Fighting off patches that try to make the deleted rvalue 
overload "do something" is _also_ maintenance work, even if it doesn't 
result in a code change.

Finally, I'll bet my behind (pardon my French) that at some point in the 
not-too-distant future someone will appear on Gerrit with a patch to 
change Q_FOREACH to use C++20 ranged-for-with-initializer:

   #define Q_FOREACH(decl, expr) \
  for (const auto _q_foreach_c = expr; decl : _q_foreach_c)

And I'd probably approve it, because then that thing can actually just 
be replaced with the expansion everywhere, and then, finally, be deleted.

B Universe, here we come!

Thanks,
Marc

[1] I need qpdfedit once every year to do my taxes, but every year it's
 getting harder to get the old laptop that still has it installed to
 run again (yes, I know of VMs)

[2] Shamelessly stolen from Isodope
 (https://youtu.be/ESAaz9v4mSU?t=514)

-- 
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] How qAsConst and qExchange lead to qNN

2022-11-07 Thread Edward Welbourne via Development
Volker Hilsheimer (7 November 2022 16:51) wrote:
> The open question is whether and when we should deprecate such a
> stop-gap 1:1 reimplementations of std functionality. How to deprecate
> is now well documented, but the wiki starts with the process of doing
> so once we concluded that we shall. It doesn’t give us any guidance
> yet on how to come to that conclusion.

And, just to be clear, that omission was entirely deliberate.  When I
wrote [[Deprecation]], I was quite sure we did not have a consensus on
the answer to that question, so what I wrote takes no position on the
controversy.  On the one hand, the benefits of getting rid of old cruft
argue for deprecating anything for which there is now a better way to do
whatever it was there for; on the other hand, disruption to users of Qt
is unwelcome, arguing for only deprecating anything when its use is
actually apt to cause harm (e.g. the old API incorporates a design bug).

For Qt to remain relevant in the rapidly-evolving C++ landscape, it
needs to change; but one of its strong selling-points has long been its
conservatism in API and ABI, that lets working code keep working.

There is thus an inevitable tension between "move forward, leaving the
past behind" and "if it ain't broke, don't 'fix' it",

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


Re: [Development] How qAsConst and qExchange lead to qNN

2022-11-07 Thread Volker Hilsheimer via Development
> On 4 Nov 2022, at 16:00, Marc Mutz via Development 
>  wrote:
> 
> Hi,
> 
> After getting my head washed by Volker, lemme provide background on 
> these two functions.

Thanks for the context, Marc!

> TL;DR: we created real maintenance and porting problems by not removing 
> stop-gap functionality in a timely fashion, qNN presented as a way to 
> ensure this won't happen again.
> 
> Both qAsConst and qExchange are 1:1 reimplementations of std 
> functionality we couldn't rely on back then (as_const is C++17 and 
> exchange is C++14), so they're exactly equivalent to the std versions. 
> Or were, when they were added.
> 
> Neither std::as_const nor qAsConst have changed, so the replacement is 
> easy: s/qAsConst/std::as_const/. It cannot not compile because qAsConst 
> comes from qglobal.h and that transitively includes . This is 
> what was intended from the get-go:


The open question is whether and when we should deprecate such a stop-gap 1:1 
reimplementations of std functionality. How to deprecate is now well 
documented, but the wiki starts with the process of doing so once we concluded 
that we shall. It doesn’t give us any guidance yet on how to come to that 
conclusion.

As qAsConst has shown, it is of course useful and pragmatic for us to introduce 
certain functionality before the respective C++ standard is generally available 
to us. Let’s then assume - for the sake of argument - that we might be able to 
require a new standard 2 years after all generally available compilers fully 
implement it (C++20 has perhaps reached that point [1] if we ignore Apple 
clang’s limping behind on library features, so let’s say 2024). Then there’ll 
likely be a lot of code in Qt that is using our temporary implementation. 
Again, qAsConst is a good example.

[1] https://en.cppreference.com/w/cpp/compiler_support

The questions is now what we do. Replacing all qAsConst with std::as_const is 
mechanically straight forward, also thanks to the tooling that Marc and others 
have been building for that purpose. So as long as we follow the proposed 
rules, then making the change as such not the problem. And neither is reviewing 
- with well-tested tooling, reviewing every line of diff produced adds little 
value.

However, such a changes will touch a lot of code, across all repositories. And 
that introduces conflicts when cherry-picking changes back to stable branches 
(and it messes up the git blame history, which for one-liners is perhaps rarely 
an issue). If we consider the s/count/size, s/qAsConst/std::as_const, 
s/QStringLiteral/u””_s, … replacement activities, then each of them might not 
be a big issue, but the sum of lines changed by all of them together quickly 
makes this become a source of bother and discontent. To a certain degree, the 
argument is the same as for making coding-style fixes: we don’t, unless we 
touch the respective code anyway.

That’s not an entirely fair comparison, because we do have to maintain our Qt 
replacements for as long as they get used in Qt. With qAsConst, maintaining 4 
lines of code would perhaps have been acceptable if it saved us that noise. 
With the 9 lines of qExchange, Marc has already pointed out that we have 
diverged from std. If we consider std::span or std::expected - we clearly don’t 
want to drag those implementations around with us for longer than absolutely 
necessary.

So, there probably can’t be a single rule that fits every situation. And 
removing something from everyone’s toolbox, like qAsConst, might anyway deserve 
creating a bit more awareness. Hence, a suggestion:


When it’s time to phase out one of our own qNN implementations, then

1) propose the change here first to raise awareness, and to give people time to 
ask questions and/or raise objections

Even if the people doing the work all agree, a lot of maintainers and 
contributors will still be impacted (at least by the tool being removed). The 
proposal should come with some data about how prevalent the usage of the 
relevant construct is in Qt. It makes a difference whether we’d have to touch a 
few dozen lines, or several hundred to remove all usage.

2) If possible, add a warning to the sanity bot so that no new usage is added

This is trivial in some cases, not so trivial in others. Rationale: For changes 
that impact a larger amount of code, there’ll be plenty of time between those 
changes getting merged, and the old Qt-implementation ultimately getting 
removed or fully deprecated (which we can’t/shouldn’t do while we still have 
usage in Qt itself). For example, we now have some qAsConst back in the qtbase 
code.


Whether we then do a bulk replacement in Qt, or whether we just stop using old 
stuff in new code and phase it out over time as we touch code (until here’s 
perhaps little enough left to make a bulk change), depends on the discussion. 
If we do make a bulk change, then making that change in stable branches to 
avoid cherry-picking conflicts would probably be ok as well 

[Development] How qAsConst and qExchange lead to qNN

2022-11-04 Thread Marc Mutz via Development
Hi,

After getting my head washed by Volker, lemme provide background on 
these two functions.

TL;DR: we created real maintenance and porting problems by not removing 
stop-gap functionality in a timely fashion, qNN presented as a way to 
ensure this won't happen again.

Both qAsConst and qExchange are 1:1 reimplementations of std 
functionality we couldn't rely on back then (as_const is C++17 and 
exchange is C++14), so they're exactly equivalent to the std versions. 
Or were, when they were added.

Neither std::as_const nor qAsConst have changed, so the replacement is 
easy: s/qAsConst/std::as_const/. It cannot not compile because qAsConst 
comes from qglobal.h and that transitively includes . This is 
what was intended from the get-go:

https://codereview.qt-project.org/c/qt/qtbase/+/114548
 > Intended as internal API, but not put into the QtPrivate
 > namespace to make it simpler to use.
 >
 > We could wait for std::as_const, but that is far, far
 > away (just entered the current C++17 draft as of this
 > writing), and the Qt containers with their tendency to
 > detach are a problem _now_.

I don't remember why sometime later we (I) documented it as public API, 
but I failed to add the magic sentence

 > Use std::as_const if you can (C++17). qAsConst will be removed is a
 > future version of Qt

We seem to be removing these kinds of sentences from API (currently on 
Gerrit for Q_FOREACH), but I don't think we should.

Anyway, that's qAsConst. It does what std::as_const does, so moving to 
The Real McCoy is trivial.


Unfortunately, as it always seems to happen, both std::exchange and 
qExchange changed. They're still 100% equivalent, but qExchange is now 
equivalent to std::exchange in C++20 (constexpr) and C++23 
(conditionally noexcept, technically a DR), so we can _not_ blindly 
replace qExchange with std::exchange, because we only require C++17.

It is important to dwell on the difference between the two:

std::as_const never changed, so qAsConst never had to.
std::exchange changed, so it was felt that qExchange had to, too.

The outcome is that we don't need to maintain qAsConst, and can 
trivially port away from it, but we have (continued) to maintain 
qExchange, and can _not_ trivially port away from it.

What was the mistake? The mistake was to not limit the qExchange 
implementation to the C++14 version, but directly going for C++20 
semantics (constexpr; mea culpa) and therefore not being able to remove 
qExchange when we _could_ (in 6.0, when a C++14-only qExchange and 
std::exchange would have been semantically identical). Instead, we kept 
dragging it on with unclear semantics (C++14 or C++20?), until C++23 
semantics were added to qExchange() extending the lifetime even further. 
Had we limited qExchange to C++14 and removed qExchange when we had the 
chance, this lock-in would not have happened.


But why do the deprecation _now_?

It turns out, when we decomposed qglobal.h to speed up compilation (a 
high-prio request from users), we ended up with a clean qglobal.h 
including only other includes, except qAsConst and qExchange for which 
we didn't find a good home. My idea, then, was to deprecate qAsConst and 
qExchange, introducing q20::exchange (see below), so that they could 
stay in qglobal.h, itself to be deprecated in favour of the more 
fine-grained headers and port the code base away from qAsConst and 
qExchange to pave the way for rolling out the finer-grained headers. It 
turns out I was too slow and qAsConst and qExchange are now in 
qttypetraits.h (go figure), and we have problems deprecating them 
_there_, because the Qt deprecation macros aren't defined, yet, when 
that file is included in some TUs.

One problem after the other. This is just to illustrate that there's a 
very real cost associated with keeping stop-gap reimplementations of std 
functionality around for longer than absolutely necessary.



So, late in Qt 5 I came to experiment with how we could break this 
vicious cycle: On one hand, we want to provide some std library 
reimplementations when we just don't want to or cannot wait for them to 
become available. But OTOH we want these to be stop-gaps, until we can 
depend on a version of C++ in which they ship, and remove them as soon 
as possible to avoid the above-mentioned trap where we need to maintain 
them indefinitely. And certainly, we don't want to follow the std 
implementation changes lest we never catch up with the std version.

For some time, I thought just making these functions private would be 
enough (qmemory_p.h for q_make_unique), but private API means we can't 
use it in public headers. Also, there's no indication when we can remove 
the stop-gap again. Was make_unique C++14 or C++17? Hmm, hmm...

Finally, there's the problem of whitespace: assume we've solved the 
problem with use in public headers, and I could write

auto p = q_make_unique(foo, ,
   bar, );

I can safely