PDA

View Full Version : stl question




danwilliams
May 3, 2011, 03:55 PM
The question is in the comment section of the code block below.


#include <list>
#include <functional>

struct Foo
{
Foo():_bar(0){}
explicit Foo(const int bar):_bar(bar){}
int Bar() const {return _bar;}
bool less_equal(const int value) {return _bar <= value;}
private:
int _bar;
};

int main(int argc, char* argv[])
{
std::list<int> myInts;
std::list<Foo> myFoos;
for (int i=0; i<5; ++i)
{
myInts.push_back(i);
myFoos.push_back(Foo(i));
}
// Remove all values that are less than 4.
myInts.remove_if(std::bind2nd(std::less_equal<int>(),3));
myFoos.remove_if(std::bind2nd(std::mem_fun_ref(&Foo::less_equal),3));
// mInts = [1](4)
// myFoos = [1]({_bar=4})

// How do I do the same as the lines above using the Foo::Bar member
// function? The following obviously does not complete succesfully.
//myFoos.remove_if(std::bind2nd(std::bind2nd(std::less_equal<int>(),3)),
// std::mem_fun_ref(&Foo::Bar));
// In other words, I want to iterate through the list of Foo instances
// and call the Bar() member function and pass it to std::less_equal.
return 0;
}



jiminaus
May 3, 2011, 04:22 PM
What you asking doesn't make sense.

What you're asking is to implement something like the following (ill)logic:

foreach (element in myFoos) {
if (element < element.less_equal(3)) {
remove element from myFoos;
}
}


You'd either use std::mem_fun_ref with your less_equal member function, or you'd use std::less_equal and define operator <= in Foo. You couldn't use both.

danwilliams
May 3, 2011, 04:49 PM
Wow! That was a quick response. Thanks jiminaus.

Hmmm. I hate being illogical. Let me ask rephrase the question. I would like to do the following:

for (std::list<Foo>::iterator it=myFoos.begin(); it!=myFoos.end(); ++it)
{
if (it->Bar() <= 3)
{
myFoos.remove(*it);
}
}


But I wanted to do all within the std::list::remove_if() function.

And the reason I don't use the Foo::less_than() function is that I don't own the Foo class and it doesn't have that implemented in my real code base. (I just added it here as an example) I can't modify Foo. All that Foo provides is the Foo::Bar() function.

jiminaus
May 3, 2011, 06:31 PM
Wow! That was a quick response. Thanks jiminaus.

Hmmm. I hate being illogical. Let me ask rephrase the question. I would like to do the following:

for (std::list<Foo>::iterator it=myFoos.begin(); it!=myFoos.end(); ++it)
{
if (it->Bar() <= 3)
{
myFoos.remove(*it);
}
}


But I wanted to do all within the std::list::remove_if() function.

And the reason I don't use the Foo::less_than() function is that I don't own the Foo class and it doesn't have that implemented in my real code base. (I just added it here as an example) I can't modify Foo. All that Foo provides is the Foo::Bar() function.

Sorry Dan. I misread your initial comment. I though you wanted to call less_equal not Bar.

I don't know how to do this with bindings. My C++ knowledge is rusty. My C++ template meta-programming knowledge is poor.

Here's a solution I came up with:

struct foo_bar_less_equal : std::binary_function<Foo, int, bool> {
bool operator ()(Foo a, int b) const {
return a.Bar() <= b;
}
};

// And then later

myFoos.remove_if(std::bind2nd(foo_bar_less_equal(), 3));


I think C++0x add stuff so you don't need the explicit class, but I don't know it.

danwilliams
May 3, 2011, 06:56 PM
Sorry Dan. I misread your initial comment. I though you wanted to call less_equal not Bar.

I don't know how to do this with bindings. My C++ knowledge is rusty. My C++ template meta-programming knowledge is poor.

Here's a solution I came up with:

struct foo_bar_less_equal : std::binary_function<Foo, int, bool> {
bool operator ()(Foo a, int b) const {
return a.Bar() <= b;
}
};

// And then later

myFoos.remove_if(std::bind2nd(foo_bar_less_equal(), 3));


I think C++0x add stuff so you don't need the explicit class, but I don't know it.
Ha! My C++ template meta-programming knowledge is poor as well. That's why I am here. ;-)

Yep. I came up with that functor too. It's fine when you just have one function method you are using in std::remove_if. But in my case there are several and coming up with a functor for each one is a maintenance headache. For example:

struct foo_bar1_less_equal ...;
struct foo_bar2_less_equal ...;
struct foo_bar3_less_equal ...;
// etc...


I was hoping to use bindings to eliminate the need to write a function object like you posted. Hmmm. Maybe I should look into boost::bind?

jiminaus
May 3, 2011, 07:01 PM
Ha! My C++ template meta-programming knowledge is poor as well. That's why I am here. ;-)

Yep. I came up with that functor too. It's fine when you just have one function method you are using in std::remove_if. But in my case there are several and coming up with a functor for each one is maintenance headache. For example:

struct foo_bar1_less_equal ...;
struct foo_bar2_less_equal ...;
struct foo_bar3_less_equal ...;
// etc...


I was hoping to use bindings to eliminate the need to write a function object like you posted. Hmmm. Maybe I should look into boost::bind?

I'd forgotten about boost. Yes try that. Boost has excellent template meta-programming tools.

Hopefully someone else here with Boost experience and at least a current working knowledge of C++ can help you better.


EDIT: Wow!! Boost is awesome!!


#include <list>
#include <functional>
#include <iostream>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

struct Foo
{
Foo():_bar(0){}
explicit Foo(const int bar):_bar(bar){}
int Bar() const {return _bar;}
bool less_equal(const int value) {return _bar <= value;}
private:
int _bar;
};

int main(int argc, char* argv[])
{
using namespace boost::lambda;

std::list<int> myInts;
std::list<Foo> myFoos;
for (int i=0; i<5; ++i)
{
myInts.push_back(i);
myFoos.push_back(Foo(i));
}

// myInts.remove_if(std::bind2nd(std::less_equal<int>(), 3));
myInts.remove_if(_1 <= 3);

// myFoos.remove_if(std::bind2nd(std::mem_fun_ref(&Foo::less_equal),3));
myFoos.remove_if(bind(&Foo::Bar, _1) <= 3);

return 0;
}


Who needs Apple's blocks when you've got Boost's lambdas?!

ShortCutMan
May 4, 2011, 05:31 AM
Here is one way to do it:
#include <list>
#include <boost/bind.hpp>

struct Foo
{
Foo():_bar(0){}
explicit Foo(const int bar):_bar(bar){}
int Bar() const {return _bar;}

static bool less_equal(const Foo& obj, int value) {return obj.Bar() <= value;};
private:
int _bar;
};

int main(int argc, char* argv[])
{
std::list<int> myInts;
std::list<Foo> myFoos;
for (int i=0; i<5; ++i)
{
myInts.push_back(i);
myFoos.push_back(Foo(i));
}
// Remove all values that are less than 4.
myInts.remove_if(std::bind2nd(std::less_equal<int>(),3));
myFoos.remove_if(std::bind2nd(std::mem_fun_ref(&Foo::less_equal),3));
// mInts = [1](4)
// myFoos = [1]({_bar=4})

myFoos.remove_if(boost::bind(&Foo::less_equal, _1, 3));

return 0;
}

There I bind the address of the function, notate (using '_1') that there is one parameter that will be passed in by the caller of the bind object, and pass in the value 3 to the second parameter. Note I also made the function static and added the parameter required by the remove_if predicate as noted in STL docs (http://www.cplusplus.com/reference/stl/list/remove_if/, I use this website at work). I haven't compiled this code, as I don't have a build of boost setup here at home, but I use it all the time at work. This problem would be a solvable using lambdas in C++0x. Boost lambdas are ugly and I personally wouldn't use nor encourage them.

jiminaus
May 4, 2011, 05:36 AM
ShortCutMan, is there a way to use boost's bind (not boost's lambda bind) without calling Foo's less_equal? That is can the call to Bar() be build into the bind expression?

The OP said subsequently that less_equal doesn't actually exist in the class he's actually trying to use, and he can't change the class. And there's apparently more than just Bar() he needs to use in these kinds of expressions.

danwilliams
May 4, 2011, 07:42 AM
ShortCutMan, is there a way to use boost's bind (not boost's lambda bind) without calling Foo's less_equal? That is can the call to Bar() be build into the bind expression?

The OP said subsequently that less_equal doesn't actually exist in the class he's actually trying to use, and he can't change the class. And there's apparently more than just Bar() he needs to use in these kinds of expressions.

jiminaus, do you ever sleep? :) And yes, (unfortunately) I can't add to the Foo class by adding Foo::less_equal().



myFoos.remove_if(boost::bind(&Foo::Bar, _1) <= 3);


Winner! Yes, that is exactly what I was hoping for (and I agree that boost is awesome). I also can't wait to upgrade our compilers to C++ 0x to take advantage of lambdas. As of now, I will look to boost.

I will now spend some quality time with stl, boost, and my compiler. Thanks to everyone.

danwilliams
May 4, 2011, 09:27 AM
Here is what I ended up with:

#include <list>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

struct Foo
{
Foo():_bar(0){}
explicit Foo(const int bar):_bar(bar){}
int Bar() const {return _bar;}
private:
int _bar;
};

int main(int argc, char* argv[])
{
std::list<Foo> myFoos;
for (int i=0; i<5; ++i)
{
myFoos.push_back(Foo(i));
}
// myFoos = [5]({_bar=0},{_bar=1},{_bar=2},{_bar=3},{_bar=4})

// Remove all values that are less than 4.
myFoos.remove_if(boost::lambda::bind(&Foo::Bar, boost::lambda::_1) <= 3);
// myFoos = [1]({_bar=4})
return 0;
}



There I bind the address of the function, notate (using '_1') that there is one parameter that will be passed in by the caller of the bind object, and pass in the value 3 to the second parameter. Note I also made the function static and added the parameter required by the remove_if predicate as noted in STL docs (http://www.cplusplus.com/reference/stl/list/remove_if/, I use this website at work). I haven't compiled this code, as I don't have a build of boost setup here at home, but I use it all the time at work. This problem would be a solvable using lambdas in C++0x. Boost lambdas are ugly and I personally wouldn't use nor encourage them.


The boost lambdas may be ugly but I would submit so is writing extra one off functors/functions. I hear what you are saying though. Unfortunately, we can't upgrade yet to C++0x to take advantage of the new built in lambda support.

This allows me to write less code. Now I must make sure I write concise comments explaining what is happening at the point of the std::list::remove_if() call.

ShortCutMan
May 4, 2011, 05:35 PM
I see what you mean also, I'm probably just used to seeing boost bind more so than boost lambda. Sometimes I have seen people write nested structs in the calling function if it is once off, or just put it in an anonymous namespace since it doesn't really need to be a class member function.

Either way, you've found a solution that works for you. :)