From: Mikosz on
Hi,

Recently I've met the need to perform operations like this quite
often:

struct A {
int val;
}

....
std::vector<A> as;
....
std::set<int> vals;
std::vector<A>::const_iterator it, end = as.end();
for (it = as.begin(); it != end; ++it) {
vals.insert(it->val);
}

that is, to perform some action on one of the members of each of the
collection's elements. I couldn't find any solutions within the STL,
so I've created my own MemberIterator class. The code is:

template<class Iterator, class PointerToMember, class Member>
class MemberIterator : public Iterator {
public:

MemberIterator(Iterator it, PointerToMember ptr) : Iterator(it),
ptr_(ptr) {
}

Member& operator*() {
return Iterator::operator*().*ptr_;
}

private:

PointerToMember ptr_;

};

template<class Member, class Iterator, class PointerToMember>
MemberIterator<Iterator, PointerToMember, Member> makeMemberIterator(
Iterator it, PointerToMember ptr) {
return MemberIterator<Iterator, PointerToMember, Member>(it, ptr);
}

Given this solution, I can write:

std::copy(makeMemberIterator<int>(as.begin(), &A::val),
makeMemberIterator<int>(as.end(), &A::val), std::back_inserter(vals));

and it works just fine.

I would like to know your opinion on
1. whether this class is implemented correctly or how I could make it
better
2. whether I should attempt to write stuff like this or use some STL
provided solution that I'm not aware of

Thanks,
Mikolaj

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Paul Bibbings on
Mikosz <mikoszrrr(a)gmail.com> writes:

> Hi,
>
> Recently I've met the need to perform operations like this quite
> often:
>
> struct A {
> int val;
> }
>
> ...
> std::vector<A> as;
> ...
> std::set<int> vals;
> std::vector<A>::const_iterator it, end = as.end();
> for (it = as.begin(); it != end; ++it) {
> vals.insert(it->val);
> }
>
> that is, to perform some action on one of the members of each of the
> collection's elements. I couldn't find any solutions within the STL,


#include <vector>
#include <set>
#include <algorithm>

struct A {
A(int v) : val(v) { } // added for convenience here
int val;
};

struct val_functor {
int operator()(A a) { return a.val; }
};

int main()
{
std::vector<A> as;
std::set<int> vals;
for (int i = 0; i < 10; i++) as.push_back(i);

std::transform(as.begin(),
as.end(),
std::inserter(vals, vals.begin()),
val_functor());
}

> so I've created my own MemberIterator class. The code is:
>
> template<class Iterator, class PointerToMember, class Member>
> class MemberIterator : public Iterator {
> public:
>
> MemberIterator(Iterator it, PointerToMember ptr) : Iterator(it),
> ptr_(ptr) {
> }
>
> Member& operator*() {
> return Iterator::operator*().*ptr_;
> }
>
> private:
>
> PointerToMember ptr_;
>
> };
>
> template<class Member, class Iterator, class PointerToMember>
> MemberIterator<Iterator, PointerToMember, Member> makeMemberIterator(
> Iterator it, PointerToMember ptr) {
> return MemberIterator<Iterator, PointerToMember, Member>(it, ptr);
> }
>
> Given this solution, I can write:
>
> std::copy(makeMemberIterator<int>(as.begin(), &A::val),
> makeMemberIterator<int>(as.end(), &A::val), std::back_inserter(vals));
>
> and it works just fine.

I wouldn't have thought that the above would work at all. Given that
your vals is of type std::set<int>, I would have expected the above line
of code to fail on std::set not defining push_back.

> I would like to know your opinion on
> 1. whether this class is implemented correctly or how I could make it
> better
> 2. whether I should attempt to write stuff like this or use some STL
> provided solution that I'm not aware of

Prefer the second option in 2. which, effectively, makes 1. moot.

Regards

Paul Bibbings

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Nick Hounsome on
On 21 June, 14:06, Mikosz <mikosz...(a)gmail.com> wrote:
> Hi,
>
> Recently I've met the need to perform operations like this quite
> often:
>
> struct A {
> int val;
>
> }
>
> ...
> std::vector<A> as;
> ...
> std::set<int> vals;
> std::vector<A>::const_iterator it, end = as.end();
> for (it = as.begin(); it != end; ++it) {
> vals.insert(it->val);
>
> }
>
> that is, to perform some action on one of the members of each of the
> collection's elements. I couldn't find any solutions within the STL,
> so I've created my own MemberIterator class. The code is:

My first thought was to make a decorator collection that presents a
(obviouly const) collection as a collection of some member.
It would work for your example but would otherwise be less flexible.

My second thought was to wonder whether you could just deduce the type
from the member pointer.

My third thought - and it should probably have been my first - is that
your result is far less readable than the original.
Maybe it's me but I find explicit loops to be more comprehensible in
90% of cases.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Mikosz on
Paul, thanks for the quick reply.

On 22 Cze, 01:35, Paul Bibbings <paul.bibbi...(a)gmail.com> wrote:
> Mikosz <mikosz...(a)gmail.com> writes:
> > [...] to perform some action on one of the members of each of the
> > collection's elements. I couldn't find any solutions within the STL,
>
> #include <vector>
> #include <set>
> #include <algorithm>
>
> struct A {
> A(int v) : val(v) { } // added for convenience here
> int val;
> };
>
> struct val_functor {
> int operator()(A a) { return a.val; }
> };
>
> int main()
> {
> std::vector<A> as;
> std::set<int> vals;
> for (int i = 0; i < 10; i++) as.push_back(i);
>
> std::transform(as.begin(),
> as.end(),
> std::inserter(vals, vals.begin()),
> val_functor());
> }
>

Good point, I actually forgot about 'transform'. However, a solution
that would allow me not to implement an extracting functor would be
much neater. Again - I don't know of any existing solutions, so I
crafted my own. What do you think of this:

template<class PointerToMember, class Member>
class Extractor {
public:

Extractor(PointerToMember ptr) :
ptr_(ptr) {
}

template<class T>
Member& operator()(T& object) {
return object.*ptr_;
}

template<class T>
const Member& operator()(const T& object) const {
return object.*ptr_;
}

private:

PointerToMember ptr_;

};

template <class Member, class PointerToMember>
Extractor<PointerToMember, Member> makeExtractor(PointerToMember ptr)
{
return Extractor<PointerToMember, Member>(ptr);
}

struct A {
A(int v) :
val(v) {
}
int val;
};

int main() {
std::vector<A> as;
std::set<int> vals;
for (int i = 0; i < 10; i++) {
as.push_back(i);
}

std::transform(as.begin(), as.end(), std::inserter(vals,
vals.begin()), makeExtractor<int>(&A::val));
std::copy(vals.begin(), vals.end(),
std::ostream_iterator<int>(std::cout, ", "));
}

>
> > std::copy(makeMemberIterator<int>(as.begin(), &A::val),
> > makeMemberIterator<int>(as.end(), &A::val), std::back_inserter(vals));
>
> > and it works just fine.
>
> I wouldn't have thought that the above would work at all. Given that
> your vals is of type std::set<int>, I would have expected the above line
> of code to fail on std::set not defining push_back.
>

Sure, that was a typing error. Should be std::inserter instead of
std::back_inserter in there.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Paul Bibbings on
Mikosz <mikoszrrr(a)gmail.com> writes:

> Paul, thanks for the quick reply.
>
> On 22 Cze, 01:35, Paul Bibbings <paul.bibbi...(a)gmail.com> wrote:
>> Mikosz <mikosz...(a)gmail.com> writes:
>> > [...] to perform some action on one of the members of each of the
>> > collection's elements. I couldn't find any solutions within the STL,
>>
>> #include <vector>
>> #include <set>
>> #include <algorithm>
>>
>> struct A {
>> A(int v) : val(v) { } // added for convenience here
>> int val;
>> };
>>
>> struct val_functor {
>> int operator()(A a) { return a.val; }
>> };
>>
>> int main()
>> {
>> std::vector<A> as;
>> std::set<int> vals;
>> for (int i = 0; i < 10; i++) as.push_back(i);
>>
>> std::transform(as.begin(),
>> as.end(),
>> std::inserter(vals, vals.begin()),
>> val_functor());
>> }
>>
>
> Good point, I actually forgot about 'transform'. However, a solution
> that would allow me not to implement an extracting functor would be
> much neater. Again - I don't know of any existing solutions, so I
> crafted my own. What do you think of this:

From the `solution' that you give below, what I understand you to be
requiring here is, not that you don't want to implement an extracting
functor as such (since, essentially, that is what you do), but that you
don't want to have to implement *separate* functors for different
classes and for different members of those classes; essentially, what
you are looking for is a /generic/ solution. Would that be right?

> template<class PointerToMember, class Member>
> class Extractor {
> public:
>
> Extractor(PointerToMember ptr) :
> ptr_(ptr) {
> }
>
> template<class T>
> Member& operator()(T& object) {
> return object.*ptr_;
> }
>
> template<class T>
> const Member& operator()(const T& object) const {
> return object.*ptr_;
> }
>
> private:
>
> PointerToMember ptr_;
>
> };
>
> template <class Member, class PointerToMember>
> Extractor<PointerToMember, Member> makeExtractor(PointerToMember ptr)
> {
> return Extractor<PointerToMember, Member>(ptr);
> }
>
> struct A {
> A(int v) :
> val(v) {
> }
> int val;
> };
>
> int main() {
> std::vector<A> as;
> std::set<int> vals;
> for (int i = 0; i < 10; i++) {
> as.push_back(i);
> }
>
> std::transform(as.begin(), as.end(), std::inserter(vals,
> vals.begin()), makeExtractor<int>(&A::val));
> std::copy(vals.begin(), vals.end(),
> std::ostream_iterator<int>(std::cout, ", "));
> }

Given the aim as I have understood it, I think that you can do just a
little better and get template argument deduction to do more for you.
Also, you don't need both of your overloads for the function call
operator. Since neither modify the object on which the operator is
called, the const version is sufficient.
Putting all this together, I might suggest something like the following,
which is, essentially, a refactoring of your idea applying a `better'
parameterization:

#include <vector>
#include <set>
#include <algorithm>
#include <iostream>
#include <iterator>

template<class T, class M>
class Extractor
{
public:
Extractor(M (T::*ptr))
: ptr_(ptr)
{ }
M operator()(const T& t) { return t.*ptr_; }
private:
M (T::*ptr_);
};

template<class T, class M> // *both* T and M can be deduced here
Extractor<T, M> makeExtractor(M (T::*ptr))
{
return Extractor<T, M>(ptr);
}

struct A
{
A(int i) : val(i) { }
int val;
};

int main()
{
std::vector<A> as;
std::set<int> vals;
for (int i = 0; i < 10; i++) {
as.push_back(i);
}
std::transform(as.begin(),
as.end(),
std::inserter(vals, vals.begin()),
makeExtractor(&A::val));

std::copy(vals.begin(),
vals.end(),
std::ostream_iterator<int>(std::cout, ", "));
}

Here, the main difference is that *both* template arguments to
makeExtractor can be deduced from the single function call argument
whereas, in your implementation, you had to provide:

makeExtractor<int>(&A::val);

Regards

Paul Bibbings

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]