Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

On adapting an iterator

27 views
Skip to first unread message

Daniel

unread,
Jan 20, 2019, 11:35:54 PM1/20/19
to
Given an iterator over std::pair<T1,T2>, and wishing to adapt it to an
iterator over

template <class T1,class T2>
struct key_value
{
T1 key;
T2 value;
};

is there anything terribly wrong with

template <class Iter>
class iterator_adapter
{
Iter it;
public:
typedef std::input_iterator_tag iterator_category;
typedef typename std::iterator_traits<Iter>::value_type::first_type T1;
typedef typename std::iterator_traits<Iter>::value_type::second_type T2;
typedef key_value<T1,T2> value_type;
typedef value_type reference;

reference operator*() { return key_value(it->first,it->second); }

Thanks,
Daniel

Paavo Helde

unread,
Jan 21, 2019, 1:26:03 AM1/21/19
to
On 21.01.2019 6:35, Daniel wrote:
> Given an iterator over std::pair<T1,T2>, and wishing to adapt it to an
> iterator over
>
> template <class T1,class T2>
> struct key_value
> {
> T1 key;
> T2 value;
> };

Iterator is over something like std::map or std::vector, not over
std::pair, so as posed the question does not make much sense. I guess
you wanted to ask how to adapt an iterator pointing to std::pair to an
iterator pointing to key_value.

> is there anything terribly wrong with
>
> template <class Iter>
> class iterator_adapter
> {
> Iter it;
> public:
> typedef std::input_iterator_tag iterator_category;
> typedef typename std::iterator_traits<Iter>::value_type::first_type T1;
> typedef typename std::iterator_traits<Iter>::value_type::second_type T2;
> typedef key_value<T1,T2> value_type;
> typedef value_type reference;
>
> reference operator*() { return key_value(it->first,it->second); }

As written, this is not an iterator as it is lacking an increment
operation (would be easy to add though). Also, the 'reference' type is
not a reference, misleading the user. This also means that this adaptor
can only be used as InputIterator, which severely limits its usability.

My question is what's the point? Basically what you have written here
just converts a std::pair to key_value. Why do you want this to happen
inside an iterator? I would iterate over the collection normally and
just convert the elements inside the loop as needed.


Daniel

unread,
Jan 21, 2019, 9:31:06 AM1/21/19
to
On Monday, January 21, 2019 at 1:26:03 AM UTC-5, Paavo Helde wrote:
> On 21.01.2019 6:35, Daniel wrote:
> > Given an iterator over std::pair<T1,T2>, and wishing to adapt it to an
> > iterator over
> >
> > template <class T1,class T2>
> > struct key_value
> > {
> > T1 key;
> > T2 value;
> > };
>
> Iterator is over something like std::map or std::vector, not over
> std::pair,

Over the elements in an associative map of type std::pair<const T1,T2>, or
over the elements in a container of any sort where they come as
std::pair<T1,T2> or std::pair<const T1,T2>. I'm sorry if that wasn't
obvious.

> > is there anything terribly wrong with
> >
> > template <class Iter>
> > class iterator_adapter
> > {
> > Iter it;
> > public:
> > typedef std::input_iterator_tag iterator_category;
> > typedef typename std::iterator_traits<Iter>::value_type::first_type T1;
> > typedef typename std::iterator_traits<Iter>::value_type::second_type T2;
> > typedef key_value<T1,T2> value_type;
> > typedef value_type reference; // (*)
> >
> > reference operator*() { return key_value(it->first,it->second); }
>
> As written, this is not an iterator as it is lacking an increment
> operation (would be easy to add though).

I'm sorry if it wasn't obvious that I'm highlighting the typedef of the
reference in the context of a LegacyInputIterator. My apologies.

> Also, the 'reference' type is not a reference, misleading the user.

I'm sure you're aware that boost many times uses

#typedef value_type reference;

in other contexts for technical reasons. And misleading in what sense?
There are actually a fair number of places in boost where X::reference does
not equate to &X::value_type, for technical reasons. For what reason
would a user expect that? A user is right to expect member types to have
properties that satisfy the requirements of the language, but no more.
My question was about whether the line (*) above violated any formal
properties of a LegacyInputIterator.

> This also means that this adaptor can only be used as InputIterator,

I included the LegacyInputIterator tag in my fragment to frame that as the
context.

> My question is what's the point? Basically what you have written here
> just converts a std::pair to key_value. Why do you want this to happen
> inside an iterator?

> I would iterate over the collection normally and
> just convert the elements inside the loop as needed.

Taken literally, I don't think that makes sense, as a user
wouldn't have access to the implementation details of a class
in another library that takes a pair of iterators over key_value<T1,T2>'s
in a constructor, or an insert statement. Typically that's the context
when you require an adapter, when you have one thing, and need another.
I apologize if that wasn't obvious.

Perhaps you're suggesting that the class in the other library should
support, say,

template<class InputIt, class Convert>
void insert(InputIt first, InputIt last, Convert convert)
{

and allow, perhaps

j.insert(items.begin(), items.end(),
[](const std::pair<const T1,T2>& p){return
key_value<T1,T2>(p.first,p.second);});

Thanks,
Daniel


Paavo Helde

unread,
Jan 21, 2019, 11:41:14 AM1/21/19
to
On 21.01.2019 16:30, Daniel wrote:

> I included the LegacyInputIterator tag in my fragment to frame that as the
> context.

Sorry, it seems I overlooked that. Now it makes much more sense.

>> My question is what's the point? Basically what you have written here
>> just converts a std::pair to key_value. Why do you want this to happen
>> inside an iterator?
>
>> I would iterate over the collection normally and
>> just convert the elements inside the loop as needed.
>
> Taken literally, I don't think that makes sense, as a user
> wouldn't have access to the implementation details of a class
> in another library that takes a pair of iterators over key_value<T1,T2>'s
> in a constructor, or an insert statement. Typically that's the context
> when you require an adapter, when you have one thing, and need another.
> I apologize if that wasn't obvious.

If the other library cannot be modified, then your solution is indeed
the way to go. However, the needed modifications are minor and make the
other library more generic, see below.


>
> Perhaps you're suggesting that the class in the other library should
> support, say,
>
> template<class InputIt, class Convert>
> void insert(InputIt first, InputIt last, Convert convert)
> {
>
> and allow, perhaps
>
> j.insert(items.begin(), items.end(),
> [](const std::pair<const T1,T2>& p){return
> key_value<T1,T2>(p.first,p.second);});

This now means that the first library must know about the internal
details of the other library like key_value. IMHO a better design is to
define insert() in the other library such a way that it works with more
generic iterator types and converts the data itself into its internal
key_value or whatever:

template <class T1, class T2>
struct key_value {
T1 key;
T2 value;
key_value(const std::pair<T1, T2>& init)
: key(init.first), value(init.second) {}
};

template <class T1, class T2>
void foo(const key_value<T1, T2>& x) {
// do something with key_value
}

template<class InputIt>
void insert(InputIt first, InputIt last) {
while (first!=last) {
foo(key_value(*first++));
}
}

This now supports iterators to key_value as well as to all types
convertible to key_value.

(note: as written, the above requires C++17 for class template argument
deduction).


Daniel

unread,
Jan 21, 2019, 1:18:02 PM1/21/19
to
Thanks, that's helpful feedback.

Daniel
0 new messages