News for hackers. Stuff that matters.
Re:Writing a custom iterator in modern C++ by arcticbull on Sunday December 20, 2020 @09:24PM (#25491553)
There's six different kinds of iterator?!

Off hand, is it just me or...

- Isn't an iterator that outputs data to its recipient container not an iterator, but rather some type of filling API?

- Isn't a random access iterator not an iterator but rather a slice view?

- Isn't a contiguous iterator a leaky abstraction in that an iterator is just an interface that shouldn't expose how the data backing it is stored?

This iterator kindedness feels very C++ in the worst way possible. IMO the only iterator type that makes any sense is an "Input Iterator" and everything else is some sort of hot mess of inconsistency and, uh, C++.

Even with enough time I'm not sure I could design a worse API.

Re:Writing a custom iterator in modern C++ by gpderetta on Sunday December 20, 2020 @10:29PM (#25491884)
> Isn't an iterator that outputs data to its recipient container not an iterator, but rather some type of filling API?

Sure, but it makes sense for this api to have the same interface as an iterator so that existing iterators can be used without an adapter.

> Isn't a random access iterator not an iterator but rather a slice view?

It is an iterator that supports additional functions. You can use a random access oterators with functions that take output iterators, forward iterators, multipass iterators etc. Of you had untelated concepts for each use case the system would be more complex.

> Isn't a contiguous iterator a leaky abstraction in that an iterator is just an interface that shouldn't expose how the data backing it is stored?

Seeing thought abstractions and specializing algorithms is what makes STL style generic code efficient.

And it is not inconsistent at all. Stepanov has written in grat details about the theoretical fundations of his design.

Re:Writing a custom iterator in modern C++ by millstone on Sunday December 20, 2020 @11:01PM (#25492016)
Iterators are inconsistent, but this plays to the strengths of C++'s duck-typed generics. The same code can either copy or insert, depending on whether you use std::begin or std::back_inserter; this isn't principled but it's powerful.

The "six types of iterators" overstates it. You don't need to sweat those details unless you are writing boost or something. Just add what you need.

I miss C++ iterators in other languages. For example, in Rust it's hard to talk about "a position in a String" which may be advanced or retreated. Usually you give up, use an integer index, and this is much worse.

Re:Writing a custom iterator in modern C++ by Tyr42 on Monday December 21, 2020 @12:15AM (#25492321)
Backing up a utf8 stream is nontrivial though.
Re:Writing a custom iterator in modern C++ by millstone on Monday December 21, 2020 @01:31AM (#25492602)
This is an interesting point. Walking backwards through a UTF8 byte stream is easy: just look for the next non-continuation byte, and Rust really can do this, though the API is buried under `str.chars().rev()`.

C++ is structured around iterators that point into; this enables e.g. writing into the guts of a std::map but not the guts of a std::string if you care about Unicode, since the write might change the string's byte length.

Re:Writing a custom iterator in modern C++ by shakna on Sunday December 20, 2020 @09:30PM (#25491579)
> Isn't a random access iterator not an iterator?

Why would it not be? It still allows you to iterate over an entire collection, however the order is left undefined so that certain performance characteristics can be exposed to the underlying implementation.

This is the kind of tool you'd reach for if you don't care about the order of a collection, but you do care about performance, and probably care about the entire collection.

For example _pairs_ from Lua, used to iterate over key/values in tables, is a random-access iterator.

Traversal of the collection is still happening.

Re:Writing a custom iterator in modern C++ by millstone on Sunday December 20, 2020 @10:45PM (#25491953)
Random access iterators mean you can efficiently advance or retreat an iterator by an arbitrary amount. So a vector (via pointer arithmetic) but not a linked list.
Re:Writing a custom iterator in modern C++ by arcticbull on Sunday December 20, 2020 @11:53PM (#25492231)
Right but why should this matter at the type level? All you need to implement is next() and annotate it with documentation of the performance characteristics.
Re:Writing a custom iterator in modern C++ by millstone on Monday December 21, 2020 @01:48AM (#25492664)
Yeah, this is a good question. Scott Myers has a talk where he pokes fun at the "guaranteed O(N log N)" sort of a linked list, where the guarantee is in number of comparisons and not the (very large) number of pointer chases.

Why describe at the type level the difference between a backwards pointer chase and a pointer decrement? The hope is: if you know at compile time that your iterator is efficient in this way, you can pick a more specialized algorithm, say, quicksort instead of mergesort. C++ really does enable this at the type level through template specialization. In practice the STL collections haven't lived up to this, and I think the iterator tags are mostly deadweight.

Re:Writing a custom iterator in modern C++ by arcticbull on Monday December 21, 2020 @02:06AM (#25492750)
Thanks, that was helpful. I suspect the reason it ends up being dead weight is that it doesn't make much sense to prevent an API customer from using an iterator with certain performance characteristics with certain algorithms. Ultimately, the onus is on the developer to know something about the data; for instance, why wouldn't I be allowed to use any iteration algorithm on my slow-to-increment iterator if I know the length is 1?

So long as it's safe I think it's on the designer to make sure it's efficient, and I suspect that's where the API has gravitated to.

Re:Writing a custom iterator in modern C++ by missblit on Monday December 21, 2020 @12:21AM (#25492333)
You can think of it as implementing `seek`, not just `next`.

`RandomAccessIterator` implements the `+=` and `-=` operations that `BidirectionalIterator` does not. Algorithms that accept `RandomAccessIterator` can then assume that calling += is possible.

Re:Writing a custom iterator in modern C++ by arcticbull on Monday December 21, 2020 @12:40AM (#25492410)
What's the advantage to using a random access iterator over a get() indexed? Like to me the point of an iterator is that it abstracts over access kind, and exposes the minimum viable view, which is sequential forward and possibly backward access.

If you want indexed access why wouldn't you just, you know, use anything that implements operator[]?

Re:Writing a custom iterator in modern C++ by missblit on Monday December 21, 2020 @12:58AM (#25492478)
Just that iterators is the API that C++ settled on for doing this in a generic way (well concepts in C++20, but I haven't learned that yet).

It's totally 100% kosher to write a function that calls `.at()` instead of `operator+=`. As long as it only needs to work on vectors, strings, or other stuff with a `.at()` method.

But if the function works on iterators instead it can work on ANY data structure that also fulfills the requirements of RandomAccessIterator API. String Views, raw pointers, a 3rd party Cord library, etc.

There's no inherent meaning to the iterator API requiring `operator+=` instead of something closer to `get()`, except that it means that the iterator itself doesn't need to know anything about container bounds (like I said above, raw pointers work as RandomAccessIterators).

Re:Writing a custom iterator in modern C++ by Too on Monday December 21, 2020 @06:00AM (#25493805)
[] alone doesn't imply that the view is sequential. Binary search for example requires both sequential items and indexed access. You can't binary search an std::map even if it implements [].
Re:Writing a custom iterator in modern C++ by josefx on Monday December 21, 2020 @06:42AM (#25493992)
You need an additional API to create views/sublists if you want to transparently operate on sub ranges and if you want to return a found index you may need to convert its value to apply to a parent view.
Re:Writing a custom iterator in modern C++ by arcticbull on Sunday December 20, 2020 @09:44PM (#25491656)
The way the random access iterator was described in the article is not that it allows iteration over an un-ordered collection but rather that it allows random access within the collection. This would not necessarily map well to an un-ordered collection. This appears to be born out by the API which supports arbitrary addition and subtraction to the iterator. I could of course be mistaken.

What you're describing based on my experience with other languages is simply an "input iterator" over an un-ordered collection. An iterator does not guarantee deterministic/repeatable ordering, just defined ordering (as in, guarantees it won't visit the same element twice), in my experience.

Re:Writing a custom iterator in modern C++ by glouwbug on Sunday December 20, 2020 @09:51PM (#25491699)
Well, I mean, iterating over a deque, map, vector, and list, in its own, already require the pointer to the next element to decide whether to increment by the type of its size, reference the `next` pointer, or the left/right pointer, or a combination of any of the above 3. It's not the language's fault, it's computer science
Re:Writing a custom iterator in modern C++ by quietbritishjim on Sunday December 20, 2020 @10:36PM (#25491910)
You seem to have mixed up an objection to the API with an objection to the user of the word "iterator" for something a bit more general than the normal/original meaning. That conflation isn't terribly helpful. Words can have more than one meaning. So long as everyone's on the same page, that's OK.
Re:Writing a custom iterator in modern C++ by creato on Sunday December 20, 2020 @10:11PM (#25491788)
It's funny how two of the most common bandwagon posts you can see on HN and other programming venues is:

* C++ is a terrible bloated language that no one should use.

* The power of modern computers is wasted on lazy software engineers writing software with too many layers of abstraction.

Re:Writing a custom iterator in modern C++ by mehrdadn on Monday December 21, 2020 @01:13AM (#25492538)
I mean, C++ has lots of compile-time abstractions, but unlike with other languages, it can collapse a lot of them during compilation via its inlining and other optimizations. You don't get that with a language like Python...
Re:Writing a custom iterator in modern C++ by adwn on Monday December 21, 2020 @01:34AM (#25492613)
I don't see a contradiction between these two statements. There are more languages than C++ and Python. You could use Rust, or some of the languages targeting the JVM or the CLI.
Re:Writing a custom iterator in modern C++ by david2ndaccount on Sunday December 20, 2020 @10:14PM (#25491803)
To many, these statements are saying the same thing. Your average C++ codebase is a bloated mess with too many layers of abstraction.
Re:Writing a custom iterator in modern C++ by mzsmyk on Sunday December 20, 2020 @10:42PM (#25491938)
You don't need to use a "terribly bloated language" to write efficient software.
Re:Writing a custom iterator in modern C++ by brighton36 on Sunday December 20, 2020 @09:42PM (#25491646)
C/C++ may be better understood as a template processing system around assembly.
Re:Writing a custom iterator in modern C++ by robscallsign on Sunday December 20, 2020 @09:52PM (#25491707)
Keep in mind that most of the details you mentioned are for the developer of the library, not the end user/consumer of the library.

Ultimately most consumers of the library care if they can call std::sort(YourSpecialContainer.begin(), YourSpecialContainer.end()); in a consistent manner with some semblance that the use of templates and interfaces result in nearly zero overhead of the abstraction.

Re:Writing a custom iterator in modern C++ by vardump on Monday December 21, 2020 @03:41AM (#25493180)
"nearly zero overhead"

I can't call anything "nearly zero overhead" that can generate large amounts of code. Not at least with a pure conscience.

I've seen enough many cases of better performance with more "overhead" but drastically smaller code size.

Re:Writing a custom iterator in modern C++ by flqn on Monday December 21, 2020 @11:47AM (#25496623)
Code size is not always a good performance metric
Re:Writing a custom iterator in modern C++ by vardump on Monday December 21, 2020 @11:49AM (#25496653)
Nothing is a good performance metric apart from profiling. Preferably with runtime conditions (CPU load, memory bandwidth, etc.) closely mirroring production setting(s).

It's very easy to forget things like memory bandwidth, inter-core/CPU links, etc. are all limited resources. And that on real systems resources are shared.

Re:Writing a custom iterator in modern C++ by benibela on Monday December 21, 2020 @01:57PM (#25498112)
I usually profile everything with valgrind callgrind
Re:Writing a custom iterator in modern C++ by benibela on Monday December 21, 2020 @10:34AM (#25495813)
Usually people say std::sort is faster than qsort
Re:Writing a custom iterator in modern C++ by vardump on Monday December 21, 2020 @11:46AM (#25496604)
It's true whenever the function call overhead for the comparison operation dominates. Inlining a trivial operation (often just one instruction) is one extreme.

At the other extreme L1C cache (typically just 32 kB) gets constantly trashed by numerous type specialized functions. Individually fast when microbenchmarked, but collectively slow.

That's why profiling is a must when high performance is required. Microbenchmarks can be very misleading.

Re:Writing a custom iterator in modern C++ by gpderetta on Tuesday December 22, 2020 @09:06AM (#25506362)
Why would all template specializations for be in L1?

Even assuming you actually have multiple specializations in the same binary, which is not a given, only the one being in use or that it will be used very soon will be in L1. Instruction cache prefetching is extremely effective.

Re:Writing a custom iterator in modern C++ by flqn on Monday December 21, 2020 @11:45AM (#25496601)
It is, since the compiler can inline the comparator.
Re:Writing a custom iterator in modern C++ by vardump on Monday December 21, 2020 @09:14AM (#25495085)
Sorry for answering my own reply.

Zero overhead cult is pretty strong. People should profile instead. Sigh.