Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adaptors: returning underlying container iterator (root) of adapted range (view) #114

Open
trueqbit opened this issue Oct 23, 2020 · 0 comments

Comments

@trueqbit
Copy link

trueqbit commented Oct 23, 2020

When I keep using iterators of adapted ranges (aka views) I find it very useful not having to use range_iterator::base() in order to access the underlying container iterator and currently iterated object.

I often find code like

if (auto rng = find<return_found_end>(c | transformed(std::bind(&Object::name, _1)), v))
{
    const Object& o = *rng.begin().base();
    // ...
}
// or
if (auto it = find(c | transformed(std::bind(&Object::name, _1)), v);
    it.base() != c.end())
{
    const Object& o = *it.base();
    // ...
}

Yes, it's just a matter of adding the call to .base(), however I always find less clutter to be worthwhile. The issue gets more obvious the more adaptors get chained.
Instead the intent can be expressed by additional enum range_return_value values.

Currently this is exactly what I am doing, using this header file:

#pragma once
#include <boost/range/detail/range_return.hpp>


namespace kj
{

inline constexpr auto return_base_found = boost::range_return_value(boost::return_begin_end + 1);
inline constexpr auto return_base_found_end = boost::range_return_value(return_base_found + boost::return_found_end);

}

namespace boost
{

template<typename SinglePassRange>
struct range_return<SinglePassRange, kj::return_base_found>
{
    using iterator = typename range_iterator<SinglePassRange>::type;
    using type = typename iterator::base_type;

    static type pack(iterator found, SinglePassRange&)
    {
        return found.base();
    }
};

template<typename SinglePassRange>
struct range_return<SinglePassRange, kj::return_base_found_end>
{
    using iterator = typename range_iterator<SinglePassRange>::type;
    using base_iterator = typename iterator::base_type;
    using type = boost::iterator_range<base_iterator>;

    static type pack(iterator found, SinglePassRange& rng)
    {
        return type(found.base(), boost::end(rng).base());
    }
};

}

But extending the range_return handling in this way isn't perfect, and I think others can benefit as well.

That's why I propose extending enum range_return_value with the following enum values:

return_root_found,
return_root_next,
return_root_prior,
return_root_begin_found,
return_root_begin_next,
return_root_begin_prior,
return_root_found_end,
return_root_next_end,
return_root_prior_end,
return_root_begin_end,

At the very least I'd like to see a function, e.g. named root_of(), which walks the adaptor iterator chain and returns the root iterator, such that ...

    const Object& o = *root_of(it);

... works transparently no matter how many iterator are layered on top of each other.

@trueqbit trueqbit changed the title Adaptors: returning underlying container iterator (base) of adapted range (view) Adaptors: returning underlying container iterator (root) of adapted range (view) Oct 26, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant