On 06/01/2023 17:28, Jason McKesson via Std-Proposals wrote:
>> so that we can do stuff like:
>>
>> string str("monkey5");
>> Func( string_view(str).remove_suffix(1u).remove_prefix(2u) );
> You could just do this:
>
> Func(string_view(str.begin()+1, str.end() - 2));
>
> This requires C++20 for the contiguous iterator support, but it does
> work. Note that the remove_* functions exhibit UB if you try to remove
> more characters than exist. So the fact that this too will exhibit UB
> in those situations is fine.
It's completely anti-ergonomic to have to do math like that.
Compare with something like Qt, where string classes have functions that
mutate in place and functions that return the mutated version:
view.chop(N); // mutates in-place, returns void
view.chopped(N); // does not mutate, returns a new view
I agree with all three guidelines stated so far:
(1) Math is un-ergonomic.
(2) Active-verb and past-tense/adjectival-form (sort/sorted, chop/chopped, strip/stripped, trim/trimmed) are the Right Solution.
(3) Having a method that both mutates and returns reference-to-*this is just asking for misuse, and should be avoided at all costs.
But this particular operation has two unusual problems: we have to think about consistency with the methods that already exist (problem for (2)), and the operation itself is not simple (problem for (1)).
After all, `string_view` already has a substring method — it's called `substr`!
Func(str.substr(1, str.size() - 3));
This is shorter than Jason's
Func(std::string_view(str.begin()+1, str.end()-2)); // ...or...
Func(std::string_view(str.data()+1, str.size()-3));
but it is equally as mathy and un-ergonomic.
The Best Solution, which C++ will probably never get, is for negative indices to be treated Python-style, so we could write something like
Func(str.fromto(1, -2));
But that's still got trouble because "the operation is not simple." Is that second argument a position or a length? I tried to indicate "position" by naming the method `fromto`... but `fromto` is a terrible name a-priori.
Basically, no small tweak seems appropriate here. You can't even apply guideline (2), because what even is the past tense of "remove_prefix"?
Func(str.removed_prefix(1).removed_suffix(2)); // ??
Func(str.remove_prefixed(1).remove_suffixed(2)); // ??
Func(str.after_removing_prefix(1).after_removing_suffix(2)); // ??
The mutators have such unwieldy names that the non-mutators are constrained (by consistency) to also have unwieldy names and thus probably go unused.
Finally, I note that in C++20 Ranges the `views::take` and `views::drop` adaptors special-case `string_view`, so all you need is to wait for `drop_last`. Circa C++26-ish you might be able to finally write
Func(str | std::views::drop(1) | std::views::drop_last(2));
And then it's relatively easy to write your own "un-piping wrapper" that would let you use nice object-method syntax with all the standard view adaptors:
Func(+UnPipe(str).drop(1).drop_last(2));
In fact, someone's probably done a library like that already, right? If not, somebody has a niche to fill! :)
my $.02,
Arthur