[Development] Qt UDL operators

Sona Kurazyan sona.kurazyan at qt.io
Wed Mar 30 15:44:01 CEST 2022


I'd like to continue the discussion started by Marc and try to find out what should be the "fate" of our literal operators.

For the reasons explained by Marc, we've added _L1 to Qt::inline Literals::inline StringLiterals namespace, but we still have the literals operators _qs (for QString) and _qba (for QByteArray) in the global inline QtLiterals namespace. This is clearly inconsistent, so the next logical step is to move _qs to _qba to Qt::inline Literals::inline StringLiterals too. This assumes:

  *   Adding Qt::inline Literals::inline StringLiterals ::{_s, _ba} (https://codereview.qt-project.org/c/qt/qtbase/+/401308)
  *   Deprecating _qs and _qba (https://codereview.qt-project.org/c/qt/qtbase/+/401604)

As you can see, https://codereview.qt-project.org/c/qt/qtbase/+/401604 raised a lot of discussion, and not everyone felt comfortable with deprecating (and later removing) the "q"-prefixed UDLs.
To summarize the discussion, I see following options:

  1.  deprecate  _qs, _qba, add Qt::StringLiterals::{_s, _ba}, as suggested above (https://codereview.qt-project.org/c/qt/qtbase/+/401308 + https://codereview.qt-project.org/c/qt/qtbase/+/401604)
  2.  keep _qs, _qba, add_qL1 and remove Qt::StringLiterals::_L1 (https://codereview.qt-project.org/c/qt/qtbase/+/402948 + https://codereview.qt-project.org/c/qt/qtbase/+/402950)
  3.  keep _qs, _qba, add _qL1, keep Qt::StringLiterals::_L1, add Qt::StringLiterals::{_s, _ba} (https://codereview.qt-project.org/c/qt/qtbase/+/402948 + https://codereview.qt-project.org/c/qt/qtbase/+/401308)

I personally prefer the 1st option, but it would be nice to hear more opinions/concerns and decide how to proceed.


From: Development <development-bounces at qt-project.org> On Behalf Of Marc Mutz
Sent: Tuesday, March 29, 2022 4:31 PM
To: development at qt-project.org
Subject: Re: [Development] Renaming QLatin1String to QLatin1StringView

Hi all,

There seems to be some confusion over the form the literal operators ("UDLs") have taken: Qt::inline Literals::inline StringLiterals::_L1 instead of inline QtLiterals::_qL1.

There are several reasons for doing it this way, and it's important for any follow-up discussions to understand them, so bear with me as I lay them out:

  1.  UDLs are operators, and as operators, the usual way to call them is with unqualified calls: a + b may be seen by the compiler as NS::operator+(a, b), but that's not how we'd like to write our code. So, we'd like to use "meep"_L1, not QtLiterals::operator""_L1("meep", 4), assuming the latter is even valid C++.
  2.  All unqualified function calls in C++ always consider all overloads that are in scope. If two of them conflict (are ambiguous), as an API consumer, you have three ways to fix this:

     *   change the arguments (e.g. by casting) to avoid the ambiguity (not possible for UDLs, because the argument must always be a string, integer, or floating-point literal)
     *   use a qualified call (foo() -> NS1::foo() or NS2::foo(), depending on which one you want; undesirable for UDLs, as per point (1))
     *   change the scope so as to remove one of the conflicting overloads while leaving the other (this is what namespaces were designed for): Instead of "using namespace NS1; using namespace NS2; foo();", you do "using namespace NS1; foo();"

  1.  Seeing as (a) and (b) are not applicable for UDLs, (c) is the only option. This is why std arranges its UDLs that way, and that's why we chose to do the same for _L1. It allows API consumers to pick Qt's _L1 or some other (presumably their own), by adding or removing using directives. The disadvantage is that you cannot access these UDLs (std or Qt's) without first writing a using directive. This is a necessary evil, though:
  2.  We have prior art (_qs) which does things differently: ::inline QtLiterals::_qs. This UDL is declared in an inline namespace hanging in the global namespace. That means we don't need to write a using directive to be able to use it, but it also means API consumers cannot switch it off. It's always in-scope. If they now have a conflict, all they can do is to move the code to a separate .cpp file to include less headers or to not use the conflicting UDL. The conflict is unfixable using normal means, which is why we need to use an additona; character, the q prefix, to limit possible conflicts. But we can't eliminate them. As a physicist, it's not hard to come up with UDLs that start with q: _qcd, _quant, ... We like to think that Qt owns the q prefix, but it doesn't (qsort), so it's just a way to make clashes less likely. With KDE adopting the same strategy with a different letter, we already claim 1/13th of the available C++ namespace! The only reason why we got away with it, so far, is that we don't use very short function names like qs(), qe(), etc. But that's exactly what we've introduced with _qs, and it's why it should be fixed.
  3.  UDLs should be short at the use site, because we want people to use them. If not for their brevity, they carry no value over traditional constructor syntax: "meep"_L1 vs. QLatin1StringView("meep"). There is absolutely no sense in long UDLs like "meep"_qlatin1stringview, and even _qs vs. _s matters in that respect (it's a 50% increase in the identifier name!)
  4.  Any project should be able to decide for itself how to handle UDLs. Some may choose to require using declarations in every function or every scope a UDL is used, some may require it at namespace scope of every implementation file, some may add it to some central header (which is ok for an app, but not for a library, cf. SF.7 (https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines.html#Rs-using-directive)). The point is: we need to allow users to make their own choice. By adding our UDLs to ::inline QtLiterals, we deprive them of that choice, and we potentially break their code in unfixable ways. We even allow users to switch off 'signals' and 'slots' macros, but users wouldn't be able to switch off our UDLs.
This is why they need to be in Qt::inline Literals::inline StringLiterals. Anchoring the inline namespaces (which here are just for convenience, so you can alternatively say "using Qt::Literals" or "using Qt::StringLiterals" or "using Qt::Literals::StringLiterals") in the non-inline Qt namespace makes it possible for API consumers to switch them off.

In addition to the above the nested StringLiterals namespace gives us the flexibility to add (short!) UDLs for non-string domains, later, without conflict. E.g. std has s for seconds as well as for std::string. We should just copy what works instead of inventing creative ways to break users.


From: Development <development-bounces at qt-project.org<mailto:development-bounces at qt-project.org>> on behalf of Sona Kurazyan <sona.kurazyan at qt.io<mailto:sona.kurazyan at qt.io>>
Sent: Friday, March 11, 2022 5:26 PM
To: Macieira, Thiago <thiago.macieira at intel.com<mailto:thiago.macieira at intel.com>>; development at qt-project.org<mailto:development at qt-project.org> <development at qt-project.org<mailto:development at qt-project.org>>
Subject: Re: [Development] Renaming QLatin1String to QLatin1StringView

> BTW, shouldn't this one also have a "v" somewhere, like _qsv?

If there's a chance of adding an owning class for Latin-1 in future, it might make sense to reserve _L1 for it and add "v" for QLatin1StringView literal.
On the other hand, we can call the literal operator for the owning class _L1s, and save some typing for the QLatin1StringView users (which, I guess, would be more common to use).

Best regards,

> -----Original Message-----
> From: Development <development-bounces at qt-project.org<mailto:development-bounces at qt-project.org>> On Behalf Of
> Thiago Macieira
> Sent: Friday, March 11, 2022 4:39 PM
> To: development at qt-project.org<mailto:development at qt-project.org>
> Subject: Re: [Development] Renaming QLatin1String to QLatin1StringView
> On Friday, 11 March 2022 00:46:16 PST Sona Kurazyan wrote:
> > s. In Qt 7, QLatin1StringView will become "the real" class for Latin-1
> > string view, and QLatin1String will be kept as alias to it.
> I'd like to say I support this, at least as far as 7.0 with just the alias.
> When or if ever we drop the QLatin1String name and even if we ever add a
> new class is a subject for much later and doesn't have to be taken now. But
> unless we do the change Sona is proposing now, we that option wouldn't be
> on the table.
> > Note that Qt 6.4 introduces a literal operator""_L1 for constructing
> > Latin-1 string literals, to minimize the porting effort from
> > QLatin1String to QLatin1StringView.
> BTW, shouldn't this one also have a "v" somewhere, like _qsv?
> --
> Thiago Macieira - thiago.macieira (AT) intel.com
>   Software Architect - Intel DPG Cloud Engineering
> _______________________________________________
> Development mailing list
> Development at qt-project.org<mailto:Development at qt-project.org>
> https://lists.qt-project.org/listinfo/development
Development mailing list
Development at qt-project.org<mailto:Development at qt-project.org>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/development/attachments/20220330/1da1cea7/attachment-0001.htm>

More information about the Development mailing list