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 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. This reminds me of an interview that Bjarne gave a few years back where he recapped, and I'm paraphrasing here, that the committee usually insists on extra syntax for new features, to the detriment of users that are then agonizing over the complicated mess. All you're doing by having foo() and fooSpan() is putting the burden of using the correct form on the user instead of putting it where it belongs: in the compiler. This stuff is solved by tools, not documentation and uglified APIs. Thanks, Marc -- Marc Mutz <marc.m...@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 _______________________________________________ Development mailing list Development@qt-project.org https://lists.qt-project.org/listinfo/development