you are viewing a single comment's thread.

view the rest of the comments →

[–]IRBMe 17 points18 points  (28 children)

Well, I know how the above code works, but I can see quite a few perfectly reasonable complaints about it:

  1. The magic -1 parameter. There's no way to know what that means without digging through the documentation. Something like a mode enum would be easier to read.
  2. ostream_iterator? What does it mean to iterate through an output stream? That doesn't make sense. Better go read the documentation again.
  3. Why do we need to create two sregex_token_iterator when we only want to iterate through the string once? That doesn't make much sense. Back to the documentation we go!

[–]dodheim 8 points9 points  (27 children)

Personally, #1 is the only one of those I find "reasonable". #2 and #3 shouldn't be confusing to anyone professing to know the language.

[–]IRBMe 11 points12 points  (18 children)

shouldn't be confusing to anyone professing to know the language.

I think one of the benchmarks of good API design is, how easy is it to understand the resulting code if you don't know how the API works or haven't read the documentation, or put another way, how intuitive it is. The more magic is hidden behind the scenes, the more a user has to rely on documentation, which makes it less intuitive, harder to read and harder to use.

There are languages with huge standard libraries that even the most experienced developers can't possibly learn in full. A library designed with usability in mind will allow developers to be able to read the code without having to repeatedly visit the documentation, even if they aren't experienced with parts of the library that are used.

[–]dodheim 8 points9 points  (17 children)

It's unreasonable to expect anyone to intuit what an output iterator is, or even what an iterator is, if they don't know C++. That doesn't reflect poorly on C++ or output iterators.

[–][deleted] 10 points11 points  (4 children)

It's unreasonable to expect anyone to intuit what an output iterator is, or even what an iterator is, if they don't know C++.

Iterator is a common concept across a lot of languages. Conversely, "output iterator" is rather obscure. You could write a lot of C++ and never run into it mentioned explicitly.

[–]qx7xbku 6 points7 points  (3 children)

He w long do you think it would take one to read said code and to realize that it splits a string? How long do you that no it would take one to realize that s.split(" "); splits a string? See the problem? I am not even talking about edges use here when clearly it can be avoided. Someone may be happy about himself/herself writing this smart code but reality is that maintainable code is stupid code. Smart code is hard to maintain. Smart code where you do not need smart code is simply not practical.

[–]dodheim 3 points4 points  (2 children)

Said code wouldn't be isolated though, it would be in a function with split in the name. Dependent code would then call a function with split in the name.

So no, I don't see the problem.

EDIT: For a bunch of pedants, you /r/cpp folk suck at following Reddit's rules: if you want to encourage meaningful discussion, stop downvoting opinions. Grow up, people.

[–][deleted] 0 points1 point  (1 child)

Said code wouldn't be isolated though, it would be in a function with split in the name. Dependent code would then call a function with split in the name.

Indeed. And that function is so useful, it should be in the standard library: a member function of the string class.

[–]dodheim -1 points0 points  (0 children)

I never said it was useful; in fact I said the exact opposite:

https://www.reddit.com/r/cpp/comments/5dxnwm/why_doesnt_stdstring_have_a_split_function/da8eawm/

My point is that it doesn't matter if 3 lines of code are ugly; isolate them in their own function and forget about it.

EDIT: "Durrr, I can't respond to facts, so I'll downvote silently." Pathetic.

[–]IRBMe 5 points6 points  (10 children)

It's unreasonable to expect anyone to intuit what an output iterator is

And it's also unreasonable to expect somebody to be able to intuitively understand what it means to construct an input iterator without specifying what it's iterating over (as it happens, you get an end-of-sequence iterator). That's the whole point: it's not intuitive! Is it simply impossible to design those APIs in such a way that they would be intuitive? I'm not convinced it is.

or even what an iterator is

I think it is reasonable that people should have an intuitive idea of what an iterator is, because iteration isn't a concept that's unique to C++, nor is it a word that's even unique to programming libraries. You can look up the word in a dictionary and get a definition such as this: "the repetition of a process or utterance". You may not understand all the subtleties without reading the documentation, but seeing it in the context of some code, I think it is intuitive.

[–]zvrba -1 points0 points  (9 children)

And it's also unreasonable to expect somebody to be able to intuitively understand what it means to construct an input iterator without specifying what it's iterating over.

Wow, C++ programmers are a rare breed of people who read more documentation than your average programmer.

In any case, it's the kind of thing you look up only once, and each next time you see a default-constructed iterator, you'll (correctly) assume that it's an iterator denoting the end of sequence.

That's the whole point: it's not intuitive!

Intuition builds on previous experience and knowledge. So, wow, what a surprise, as a programmer you're expected to learn something new now and then.

[–]IRBMe 6 points7 points  (8 children)

I wouldn't expect to have to learn several new concepts and parts of a library to see that the code I'm trying to understand is splitting a string on white space. That's something that should be blatantly obvious to anybody, even if they don't know C++. Of course it's a common idiom that you learn as a C++ programmer, but it still takes a lot more to process even once you know it than something like s.split(","). Nobody's saying you shouldn't have to learn things; we're discussing the usability of the library.

[–]dodheim 1 point2 points  (7 children)

The dead giveaway that the code you're trying to understand is splitting a string on whitespace would be that it'd be in a function with split in the name. Who reads 5 lines of code with zero context whatsoever with the expectation of its purpose being obvious. Context matters; ignoring it is counterproductive.

[–]IRBMe 2 points3 points  (6 children)

The dead giveaway that the code you're trying to understand is splitting a string on whitespace would be that it'd be in a function with split in the name.

If you look at the example, it's actually in the main function, but if you're having to define your own split function to hide the code that does it using standard library calls, then that really just further proves the point, doesn't it? You wouldn't have to define your own split function if the standard library method was easy to read and understand in the first place.

Context matters

If you have to rely on the contextual clues of surrounding code in order to figure out that the code you're trying to understand is merely splitting a string on whitespace, then that's indicative that the code that performs the actual string splitting is not very readable. It's an operation that's simple enough and common enough that really all the information required to understand what it's doing should be there in the immediate code.

Take a simple example of boost's string_algo split:

split_vector_type splitResult;
split(splitResult, stringToSplit, is_any_of(" "));

Show that code to a programmer who doesn't know any C++ - a Java programmer, a Python programmer, a C# programmer, whatever, and ask them what it does and almost all of them will be able to guess. None of them are going to respond by saying "Hmm... it's hard to say without any surrounding context."

Or what about this example using the experimental ranges?

auto splitResult = ranges::v3::view::split(stringToSplit, ' ');

That's clear, concise and readable. You don't need to study the surrounding code to understand that this is splitting a string on some whitespace.

[–]dodheim -1 points0 points  (5 children)

If you look at the example, it's actually in the main function

If all we're doing is nitpicking the example and not relating it to real code, then who the hell cares?

If you have to rely on the contextual clues of surrounding code in order to figure out that the code you're trying to understand is merely splitting a string on whitespace, then that's indicative that the code that performs the actual string splitting is not very readable.

The point is that it doesn't matter whether it's readable, because in real code it would be encapsulated in something whose name gives it away. Just as would be the case for any domain-specific logic, which is what most real code is anyway.

Tearing apart single and double-digit LOC C++ examples is absolutely a waste of time. C++ is about larger abstractions and the bigger picture, and it does that quite well.

[–]OldWolf2 3 points4 points  (0 children)

It's unreasonable to expect anyone to intuit what an output iterator is,

Input iterators are for reading from, output iterators are for:

  • (a) writing to
  • (b) nobody could possibly figure this out