On Wednesday, 14 June 2023 01:52:49 PDT Marc Mutz via Development wrote: > == Why E _and_ O? == > > The reason we need E _and_ O for ordered types instead of just O is that > O needs to order the lhs w.r.t. the rhs, which generally involves > looking at all the state of the objects whereas E just needs to find > _one_ difference to be able to quickly return false. E.g., a container > can do > > bool E(~~~ lhs, ~~~ rhs) { > if (lhs.size() != rhs.size()) return false; // O cannot do this! > ~~~~ > } > > This is a very important optimization (and one reason why L1 string > literals are going to stay and not be replaced with UTF-8 ones: L1 op > UTF-16 _can_ use the size() short-cut while UTF-8 op UTF-16 cannot).
It's also why glibc added __memcmpeq and GCC and Clang can generate a call to that. Same reason why I added QtPrivate::equalStrings() different from QtPrivate::compareStrings() (internally they call ucstreq and ucstrcmp, respectively). > == Can E be spelled op==? == > > It could. However, op== cannot have extra arguments and we have at least > one use-case where E might have an additional argument: case-insensitive > string comparisons. We currently have no (public) API to express op==, > but case-insensitively. The closest we have is QString::compare(), but > that is an O, so it cannot use the size() mismatch shortcut, assuming > it's valid for case-insensitive string comparison (it is for L1, dunno > about UTF-16). Hmm... the facts are correct but I don't think that leads to the conclusion. The whole objective here is to provide op== anyway, so the fact that some classes have this implemented in another method that can do more should be irrelevant. For those, this op== can be inline and call the out-of-line function that does the relevant work. > There's also the situation where (cheap!) implicit conversions allow one > E to backfill several different op== (which, being hidden friends, don't > participate in implicit conversions themselves). If E is spelled op==, > what would the macros use to implement op==? It would be up to the class > author to supply sufficiently-many op== in the correct form. We don't > want that. We want all op== to be generated from the macros so we have > central control over their generation (providing reversed operators in > C++17, getting the signatures right, noexcept-ness, hidden-friend-ness, > ...). They can be exceptions and written manually. However, I do not object to having the op== be an inline hidden friend, calling a private static noexcept equality comparator function (whether that's inline or not, taking extra arguments or not). In fact, I like that, purely on stylistic reasons. > == O as public API == > > In C++20, O would be spelled op<=>, but this is not possible in C++17, > so O needs to be an actual named function, not an operator. The > question, and it's a rhetorical one, then becomes: should O be public > API, and the (rhetorical) answer is "yes, because C++17 users will want > to be able to access O in C++17 projects, too (in C++20, they could call > op<=>)". This includes Qt's own implementation of O's. Side note: I think we should start compiling Qt with C++20 right now, for all compilers that support it, with no possibility for the user to turn that off. There is C++20 functionality we could use in our implementations that don't affect ABI and don't constrain C++17 users. And I also don't object to providing C++20-only features, discussed on a case-by-case basis. > Applied to our situation, this means: > > - each (orderable) class supplies O as a hidden friend > - may supply it as a (non-static) member function "At a minimum" as a hidden friend. It may be public API, like QString::compare(), whether static or non-static. > And we have the calling convention: > lhs.O(rhs); > Qt::O(lhs, rhs); // if you know the type > > using Qt::O; > O(lhs, rhs); > ..... // if you don't I don't think this is necessary. I would prefer if that were only allowed through op<=>. C++17-constrained users would therefore need to know the types in question, know what the "O" function is called and how to call it (static vs non- static). And if it isn't public at all, then you're limited to the traditional relational operators (op<, op<=, op> and op>=). Mind you, we're not breaking those users in any way; we're simply not adding extra API for them. Paraphrasing you about a decade ago, "C++17 costs more". > This leads to the finding that O (and, depending on the outcome of the > first two Open Questions) E cannot be named like static binary functions > in existing classes, as, even if they are semantically compatible, > they're syntactically incompatible. > > That objectively rules out "compare" for O and "equals" for E. Conclusion invalidated due to premise rejected. I couldn't follow your logic anyway, but since the premise was no longer applicable, I didn't try very hard. > == Naming O == > > Given that compare() doesn't work, what are alternatives? Moot section. > == Naming E == > > So far, we've been using equal(). equals() doesn't work for technical > reasons, but while it'd work as a member function lhs.equals(rhs), it's > also kinda wrong if the function is taking two arguments (equals(lhs, > rhs), but there are _two_ objects). So equal() as the plural form or > equals() makes sense. There were also proposals of qEqual() (which I'd > again reserve as the CPO name of E) and areEqual() (which I find uglily > long). Why doesn't "equals" work? We've been over this in the code review: our current naming conventions would dictate "equals" even for two arguments, as the most common phrasing is "A equals B" instead of "A and B equal". When you compare things, you always compare at a minimum two of them, so there being two is implied by the act of comparing anyway. And I don't see why we can't provide both static and non- static members, like QString::compare does I agree "areEqual" is ugly. > order() and equal() have the nice property of being equally long. With > equal() typically returning bool and order() often returning auto, this > makes for a very pleasing alignment of the two functions. But after > thinking about this long and hard, I think eq() and cmp() are best, esp. > if you consider that they stand for == and <=> :) Too short to be public API. Those could only be implementation details and therefore need not be discussed. -- Thiago Macieira - thiago.macieira (AT) intel.com Cloud Software Architect - Intel DCAI Cloud Engineering
smime.p7s
Description: S/MIME cryptographic signature
-- Development mailing list Development@qt-project.org https://lists.qt-project.org/listinfo/development