[Development] How qAsConst and qExchange lead to qNN
Marc Mutz
marc.mutz at qt.io
Wed Nov 16 08:50:38 CET 2022
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<Key> 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 <marc.mutz at qt.io>
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
More information about the Development
mailing list