
Sent from my iPad
On 20 Dec 2024, at 20:37, Neil Groves via Boost
wrote: On Thu, 19 Dec 2024 at 15:30, Christian Mazakas via Boost < boost@lists.boost.org> wrote:
The case against a free function gets a lot stronger once you remember that optional in C++ still supports unchecked accesses with trivial syntax and that the most ergonomic and safe way to actually use it is via something like this:
users.find(user_id).map([](auto& user) { user.status = deactivated; return user; });
As you know I've tried seeing how far I could get with some work in Boost.Range. I appreciate this isn't the solution you are dreaming of.
I have working on a private branch (without new member functions in any associative container):
users | mapped_values(user_id) | invoke([](user_type& user) { user.status = deactivated; });
How is this better than for(auto&& user : users) if(user == user_id) deactivate(user); ? My thoughts on the pipeline code: - It took a while to figure out what it was doing. - you can’t easily place a breakpoint in it - it’s only possible with a load of cryptic template expansion - requires a deep knowledge of not only the language and template but also the helper library. Whereas the procedural code is: - absolutely obvious in its intent - easily debuggable - implemented using only the language and no template help at all - trivially teachable
To me, this looks remarkably similar to your example.
This works for std::map, std::multimap, std::unordered_map and std::unordered_multimap. I find this more readable than the original. Of course, this is for one very specific example and I lack support for many of the functions that boost::optional has that one may likely wish for when using a container that has at most 1 mapped_value per key.
mapped_values(key) is a Boost.Range adaptor similar to the existing "map_values" except that it takes a key parameter and applies equal_range to the source range before extracting the mapped_type. I did also find the map adaptors in Boost.Range to be unnecessarily fussy about const-ness propagation. I had to fix this on my branch to make any substantial progress.
I wondered if reading this example line above if it didn't look quite as horrific as perhaps you were imagining?
One unfortunate aspect of the original snippet is the boost::optional choice of the word "map" where we are also working with a concrete "map" container.
There is plenty of work to do if this is of interest to anyone. I would want to ensure there was no performance overhead, and currently I'm not entirely convinced that my initial implementation that uses the member equal_range is optimal. This is an implementation detail, which I would work through.
The value_or ( or front_or ) functionality for non-multi containers can be added in a similar manner, and be broadly usable for range algorithms.
Were this a free function, a user wouldn't be able to create this nice
fluent interface that users, by and large, seem to actually like.
For the left-to-right syntax I noticed that there has been a neat looking proposal https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2011r0.html
I'm hoping the left-to-right solution will appear by one or more of the active proposals while we make progress coping with the ways we have.
- Christian
If this is of any interest to you I'll look at putting some of this into the develop branch of Boost.Range. If it is not interesting I shall continue on my branch and build a more compelling iteration that I shall land later.
I hope this helps,
Neil Groves
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost