I found out that very often I use the following construct:
for (it = myvec.begin(); it != myvec.end(); ++it) {
if (it->name() == "myname")
break;
}
Certainly, the container the method and the constant can be of
any type.
The purpose is to have a template functor and use the
std::find_if instead of above loop.
I tried to mix something with std::mem_func and std::equal.
It looks that this is above my powers.
Thanks,
Mike.
It's a little tricky because we must deal with various types which happen to
have a method called "name". But it can be done with templates:
template <typename NAMED>
class MatchName
{
private:
std::string
m_name;
public:
MatchName(const NAMED &i_named) : m_name(i_named.name()) {}
bool
operator () (const NAMED &other) const {return other.name() == m_name;}
};
template <typename ITER, typename NAMED>
ITER
find_by_name(ITER first, ITER last, const NAMED &target)
{
return std::find_if(first, last, MatchName<NAMED>(target));
}
You call it like this:
find_by_name(v.begin(), v.end(), Person("Melvin"))
where "Person" is a class with a method called "name".
--
Cy
http://home.rochester.rr.com/cyhome/
Here's one way to do it using std::tr1::bind:
std::vector<A>::iterator i =
std::find_if
(
myvec.begin(),
myvec.end(),
std::tr1::bind
(
std::equal_to<std::string>(),
std::tr1::bind
(
&A::name,
_1
),
"myname"
)
);
You can also find bind at www.boost.org.
-Howard
Here is a candidate predicate:
template < class T,
class ConstType,
const ConstType (T::*method)() >
class EqualThruMemFunc {
public:
EqualThruMemFunc( const ConstType& value ) :
mValue( value ) { }
bool operator()( T& instance ) {
return (instance.*method)() == mValue ;
}
private:
const ConstType& mValue ;
} ;
class Foo {
public
// ...
const std::string name() ;
} ;
// ...
typedef vector<Foo> MyVecType ;
//
MyVecType myvec ;
MyVecType::iterator it =
find_if( myvec.begin(),
myvec.end(),
EqualThruMemFunc<MyVecType,
std::string,
&MyVecType::name>("myname") ) ;
I am sure there is a more elegant solution for a general case. But this
should get you started.
Later,
--
CrayzeeWulf
This could have been simplified with a function object as follows:
# include <iostream>
# include <ostream>
# include <string>
# include <vector>
class Person
{
private:
std::string Name;
public:
Person( const std::string& n ) : Name( n ) {}
std::string name() const { return Name; }
};
class MatchName
{
private:
std::string ToFind;
public:
template<typename T>
MatchName( const T& Arg ) : ToFind( Arg ) {}
template<typename U>
bool operator() ( const U& Obj ) const
{
return ToFind == Obj.name();
}
};
int main()
{
std::vector<Person> V;
V.push_back( Person( "Melvin" ) );
if( std::find_if( V.begin(), V.end(),
MatchName( "Melvin" ) ) != V.end() )
std::cout << "Found " << std::endl;
return 0;
}
Cheers.
Chris Val
it = find_if(myvec.begin(), myvec.end(),
compose1(bind2nd(equal_to<string>(), "myname"),
mem_fun_ref(&foo::name)));
Note: the above uses the non-standard, but obviously very useful,
unary_compose adaptor. An implementation of which can be found on SGI's
website.
Here is a complete program proving that the above find_if is equivalent
to your origional loop:
/*
* Portions Copyright (c) 1994 Hewlett-Packard Company
* used with permission.
*/
#include <string>
#include <iostream>
#include <vector>
#include <functional>
class foo {
std::string _name;
public:
foo( const std::string& n ): _name( n ) { }
const std::string& name() const { return _name; }
};
template <class _Operation1, class _Operation2>
class unary_compose
: public std::unary_function<typename _Operation2::argument_type,
typename _Operation1::result_type>
{
protected:
_Operation1 _M_fn1;
_Operation2 _M_fn2;
public:
unary_compose(const _Operation1& __x, const _Operation2& __y)
: _M_fn1(__x), _M_fn2(__y) {}
typename _Operation1::result_type
operator()(const typename _Operation2::argument_type& __x) const {
return _M_fn1(_M_fn2(__x));
}
};
template <class _Operation1, class _Operation2>
inline unary_compose<_Operation1,_Operation2>
compose1(const _Operation1& __fn1, const _Operation2& __fn2)
{
return unary_compose<_Operation1,_Operation2>(__fn1, __fn2);
}
int main()
{
using namespace std;
vector<foo> myvec;
myvec.push_back( foo( "joe" ) );
myvec.push_back( foo( "myname" ) );
myvec.push_back( foo( "harold" ) );
vector<foo>::iterator it;
for (it = myvec.begin(); it != myvec.end(); ++it) {
if (it->name() == "myname")
break;
}
assert( it->name() == "myname" );
vector< foo >::iterator it2 = find_if( myvec.begin(), myvec.end(),
compose1( bind2nd( equal_to<string>(), "myname" ),
mem_fun_ref(&foo::name) ) );
assert( it2 == it );
cout << "OK" << endl;
}
I feel, CrayzeeWulf's suggestion is the most close to what I
need. Definitely, the member method name() was for example only.
In fact, member methods vary also, not necessarily - it may be
also id(), or any other method, with the corresponding value type.
Mike.
This is great!
Mike.
> Here's one way to do it using std::tr1::bind:
What is std::tr1?
I've been looking for something along those lines. :)
[snip]
| > int main()
| > {
| > using namespace std;
| > vector<foo> myvec;
| > myvec.push_back( foo( "joe" ) );
| > myvec.push_back( foo( "myname" ) );
| > myvec.push_back( foo( "harold" ) );
| >
| > vector<foo>::iterator it;
| > for (it = myvec.begin(); it != myvec.end(); ++it) {
| > if (it->name() == "myname")
| > break;
| > }
| > assert( it->name() == "myname" );
| >
| > vector< foo >::iterator it2 = find_if( myvec.begin(), myvec.end(),
| > compose1( bind2nd( equal_to<string>(), "myname" ),
| > mem_fun_ref(&foo::name) ) );
| > assert( it2 == it );
| >
| > cout << "OK" << endl;
| > }
|
| I've been looking for something along those lines. :)
Yeah, but it requires another header, more work, and
besides, it's ugly :-).
Here is another example(I haven't tested it
much), that I came up with:
# include <iostream>
# include <ostream>
# include <string>
# include <vector>
class Person
{
private:
std::string Name;
public:
Person( const std::string& n ) : Name( n ) {}
std::string name() const { return Name; }
};
template<class Object, class DataType> class MatchName
{
private:
DataType ToFind;
DataType (Object::* PMF)();
public:
template<class PtrToFunc>
MatchName( const DataType& Arg, PtrToFunc (Object::* Ptr)() const )
: ToFind( Arg ), PMF( Ptr ) {}
bool operator() ( Object& Element ) const
{
return ToFind == ( Element.*PMF )();
}
// Example overload for std::vector<Person*>
bool operator() (Object*& Element) const
{
return ToFind == (Element ->* PMF)();
}
};
int main()
{
std::vector<Person> V;
V.push_back( Person( "Chris" ) );
if( std::find_if( V.begin(), V.end(),
MatchName<Person, std::string>( "Chris", &Person::name ) ) != V.end() )
std::cout << "Working with Chris :-) " << std::endl;
return 0;
}
What do you think ? Comments ?
Cheers.
Chris Val
>Howard Hinnant wrote:
>
>> Here's one way to do it using std::tr1::bind:
>
>What is std::tr1?
See section 1.3 of:
http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1540.pdf
Tom
C++ FAQ: http://www.parashift.com/c++-faq-lite/
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
Here's the code the OP was trying to avoid:
for (it = myvec.begin(); it != myvec.end(); ++it) {
if (it->name() == "myname")
break;
}
My comment is this: none of these proposals, including mine, are worth it!
--
Cy
http://home.rochester.rr.com/cyhome/
[snip]
| > What do you think ? Comments ?
| >
| > Cheers.
| > Chris Val
| >
| >
|
| Here's the code the OP was trying to avoid:
|
| for (it = myvec.begin(); it != myvec.end(); ++it) {
| if (it->name() == "myname")
| break;
| }
|
| My comment is this: none of these proposals, including mine, are worth it!
You know, when you look at it like that, you're
absolutely right - dang library :-).
Cheers.
Chris Val
Well, at least that nasty break could be removed. So could the whole
loop body.
for( it = myvec.begin( );
it != myvec.end( ) && it->name( ) != "myname";
++it );
I didn't bother when I had few similar loops. Now I have about
10 such and most probably there will be more.
So if we don't look into the header, the lines with:
if( std::find_if( V.begin(), V.end(),
MatchName<Person, std::string>
( "Chris", &Person::name ) ) != V.end() )
{
}
still look attractive, while almost don't decrease the number of
source lines.
Mike
Thanks.
Here is another example that you can play with,
but it does not handle <Person*> at this stage:
template<class Object, class Iter, class Criteria, class Method>
inline Iter Find_If( Iter Pos, Iter EndPos,
const Criteria& Value, Method FuncPtr )
{
for( Pos; Pos != EndPos; ++Pos )
if( (Pos->* FuncPtr)() == Value )
return Pos;
return EndPos;
}
int main()
{
std::vector<Person> V;
V.push_back( Person( "Chris" ) );
if( Find_If<Person>( V.begin(), V.end(),
"Chris", &Person::name ) != V.end() )
{
std::cout << "Working with Chris :-) " << std::endl;
}
Please feel free to play around, and modify it to
suit your needs - I'm sure it can be improved further :-).
Cheers.
Chris Val
> I would still consider the template solution.
>
> I didn't bother when I had few similar loops. Now I have about
> 10 such and most probably there will be more.
>
> So if we don't look into the header, the lines with:
>
> if( std::find_if( V.begin(), V.end(),
> MatchName<Person, std::string>
> ( "Chris", &Person::name ) ) != V.end() )
> {
> }
>
> still look attractive, while almost don't decrease the number of
> source lines.
You can combine the general usefullness of my solution with the ease of
use of the above solution:
template <typename Object, typename DataType>
inline unary_compose<std::binder2nd<std::equal_to<DataType> >,
std::const_mem_fun_ref_t<const DataType&, Object> >
MatchName(DataType data, const DataType&(Object::*func)() const )
{
using namespace std;
return compose1(bind2nd(equal_to<DataType>(), data),
mem_fun_ref(func));
}
"MatchName" now returns the object necessary to do the job, and is
somewhat easer to use than the above example of yours:
vector<foo>::iterator it2 = find_if( myvec.begin(), myvec.end(),
MatchName( string("myname"), &foo::name ) );
We can generalize that some more and get:
template <typename Func, typename T>
inline unary_compose<std::binder2nd<std::equal_to<T> >, Func >
MatchReturnTo( T data, Func func )
{
using namespace std;
return compose1(bind2nd(equal_to<T>(), data), func);
}
Which would work with any function, function object, member-function, or
pointer to member function that returns the same type that "data" is.
For this example it would be used thus:
vector<foo>::iterator it2 = find_if(myvec.begin(), myvec.end(),
MatchReturnTo(string("myname"), mem_fun_ref(&foo::name)));
[ snipped some interesting stuff :-),
that I will look into, thanks Daniel ].
Cheers.
Chris Val