How do I negate a find_if condition properly?

D

dave_if

I have a vector of TCard objects, I am sorting them based on the the
box field. That part works fine (finally!). I would then like to
random_shuffle all cards that are in a particular box, that is, I would
find the first card that is in box 3, then the first one after that
which isn't in box 3, and random_shuffle that range instead of the
usual begin(), end(). For the "not in box 3," I kept trying to use the
not1 function but I couldn't get anything that would compile. So I
ended up with (attempt #11):

class FindIfBox
{
public:
FindIfBox(int box): m_box(box) {}
int GetBox() { return m_box; }
bool operator() (const TCard& card) const
private:
int m_box;
};
//...
bool FindIfBox::eek:perator() (const TCard& card) const
{
// will have to be rewritten for Box 0
if (this->m_box >= 0)
return card.m_box == this->m_box;
else
return !(card.m_box == this->m_box);
}
//...
vector<TCard>::iterator boxBegin, boxEnd;

// the sort works perfectly
// not showing InLesserBox function object
stable_sort(v.begin(), v.end(), InLesserBox());

boxBegin = find_if(v.begin(), v.end(), FindIfBox(3));
boxEnd = find_if(boxBegin, v.end(), FindIfBox(-3));

//... (print vector)
// try to shuffle cards with that have box = 3
random_shuffle(boxBegin, boxEnd);

//.. (print vector - and it hasn't changed)
// by the way there are some elements after the box 3 that are in box 4

It took me two weeks to get something that would compile. I obviously
have some logic error because the vector doesn't change after the
shuffle. Maybe I have a temporary going out of scope somewhere, that's
what it usually is, right? Or a missing &.

I wanted to be able to say (and this doesn't compile)
boxEnd = find_if(boxBegin, v.end(), not1(FindIfBox(3));

Is there a way to say something like that using valid C++ so I don't
have to junk up my operator() member function in the FindInBox object?
 
D

Dietmar Kuehl

dave_if said:
class FindIfBox
{
public:
FindIfBox(int box): m_box(box) {}
int GetBox() { return m_box; }
bool operator() (const TCard& card) const
private:
int m_box;
};

According to the requirements for unary functions, this is not a
unary in the sense of STL. Unfortunately, I don't think that the
standard really spells out the requirements for unary functions
explicitly. Essentially, if 'UF' is unary function type, 'uf' is
an object thereof, 'A' is the argument type, 'a' an object thereof,
and 'R' the result type, the following shall be legal;

Expression Type Semantic
UF::argument_type A type of the argument
UF::result_type R type returned by the function call
uf(a) R function call

Effectively, these requirements turn normal functions into useless
function objects if they are indeed imposed. Many algorithms and
uses of function object have relaxed requirements which do not
need the associated types. However, the current function adaptors
normally do have these requirements which is the reason why you
you have to use 'ptr_fun()', 'mem_fun()', etc. if you want to
adapt normal functions.

The following program compiled fine for me and I'm confident that
it does what you want it to do:

#include <iostream>
#include <string>
#include <vector>
#include <functional>
#include <algorithm>


struct TCard
{
int m_box;
};

class FindIfBox
{
public:
typedef TCard argument_type;
typedef bool result_type;

FindIfBox(int box): m_box(box) {}
int GetBox() { return m_box; }
bool operator() (const TCard& card) const
{
return this->m_box == card.m_box;
}
private:
int m_box;
};

int main()
{
std::vector<TCard> v;

std::vector<TCard>::iterator boxBegin
= std::find_if(v.begin(), v.end(), FindIfBox(3));
std::vector<TCard>::iterator boxEnd
= std::find_if(boxBegin, v.end(), std::not1(FindIfBox(3)));
}
 
A

Alan Johnson

Dietmar Kuehl wrote:
[only relevant part quoted]
According to the requirements for unary functions, this is not a
unary in the sense of STL. Unfortunately, I don't think that the
standard really spells out the requirements for unary functions
explicitly. Essentially, if 'UF' is unary function type, 'uf' is
an object thereof, 'A' is the argument type, 'a' an object thereof,
and 'R' the result type, the following shall be legal;

Expression Type Semantic
UF::argument_type A type of the argument
UF::result_type R type returned by the function call
uf(a) R function call

Effectively, these requirements turn normal functions into useless
function objects if they are indeed imposed.

The standard provides a mechanism for dealing with this in <functional>
called std::ptr_fun, which returns an object of type
std::pointer_to_unary_function. So if you have a function, example --

int sqr(int x) { return x*x ; }

-- you could adapt it to meet the requirements of a unary function with
std::ptr_fun(sqr).

-Alan
 
C

Csaba

I have a vector of TCard objects, I am sorting them based on the the
box field. That part works fine (finally!). I would then like to
random_shuffle all cards that are in a particular box, that is, I would
find the first card that is in box 3, then the first one after that
which isn't in box 3, and random_shuffle that range instead of the
usual begin(), end(). For the "not in box 3," I kept trying to use the
not1 function but I couldn't get anything that would compile. So I
ended up with (attempt #11):

class FindIfBox
{
public:
FindIfBox(int box): m_box(box) {}
int GetBox() { return m_box; }
bool operator() (const TCard& card) const
private:
int m_box;
}; [snip]

I wanted to be able to say (and this doesn't compile)
boxEnd = find_if(boxBegin, v.end(), not1(FindIfBox(3));

Is there a way to say something like that using valid C++ so I don't
have to junk up my operator() member function in the FindInBox object?

Try deriving FindIfBox from std::unary_function< TCard, bool >, like this:

struct TCard
{
int m_box;
};

struct InLesserBox : public std::binary_function< TCard, TCard, bool >
{
bool operator()(const TCard& card1, const TCard& card2) const
{
return card1.m_box < card2.m_box; // for example
}
};

struct FindIfBox : public std::unary_function< TCard, bool >
{
FindIfBox(int box) : m_box(box) {} // as before
bool operator()(const TCard& card) const
{ return m_box == card.m_box; }
private:
int m_box;
};


int main(int argc, char* argv[])
{
std::vector<TCard> v;
std::vector<TCard>::iterator boxBegin, boxEnd;

// the sort works perfectly
// not showing InLesserBox function object
stable_sort(v.begin(), v.end(), InLesserBox());

boxBegin = find_if(v.begin(), v.end(), FindIfBox(3));
boxEnd = find_if(boxBegin, v.end(), not1(FindIfBox(3)));
// note number of )))
//boxEnd = find_if(boxBegin, v.end(), FindIfBox(-3));

//... (print vector)
// try to shuffle cards with that have box = 3
random_shuffle(boxBegin, boxEnd);

//.. (print vector - and it hasn't changed)
// by the way there are some elements after the box 3 that are in box 4

return 0;
}


Technically, FindIfBox is a binary_function< TCard, int, bool > and you
should use it with bind2nd, but a unary_function with a constructor is
simpler.

// Note: no constructor, no member variable
struct FindIfBox2 : public std::binary_function< TCard, int, bool >
{
bool operator()(const TCard& card, int box) const
{ return box == card.m_box; }
};

boxEnd = find_if( boxBegin, v.end(),
std::not1( std::bind2nd( FindIfBox2(), 3 ) )
);
 
D

dave_if

Thanks, Dietmar! What a difference those typedefs make! I remember
seeing that in Bjarne's C++PL book but I didn't know those were
actually required. So this is the solution I decided to use, because I
put those 2 typedefs in and fixed the function object and now I can do
what I want.

Csaba, I'll look closely at your code too. I tried to use the bind2nd
function before, but without properly deriving my FindIfBox from
binary_function<>. I remember reading dire warnings from Bjarne saying
to use those unary_function and binary_function templates or else
learn the hard way why they are important, but I couldn't figure out
how to use them, and I couldn't find any sample code other than
Bjarne's that used them.

Thanks Alan for your insights too. At one time I had a FindIfBox
function as part of the TCard class, I never could get the mem_fun or
mem_fun_ref to work either. Probably need to spend a few bucks and get
Josuttis' book, but I'm postponing it as long as I can!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top