views:

683

answers:

2

I recently ran into a problem that I thought boost::lambda or boost::phoenix could help be solve, but I was not able to get the syntax right and so I did it another way. What I wanted to do was remove all the elements in "strings" that were less than a certain length and not in another container.

This is my first try:

std::vector<std::string> strings = getstrings();
std::set<std::string> others = getothers();
strings.erase(std::remove_if(strings.begin(), strings.end(), (_1.length() < 24 &&  others.find(_1) == others.end())), strings.end());

How I ended up doing it was this:

struct Discard
{
    bool operator()(std::set<std::string> &cont, const std::string &s)
    {
     return cont.find(s) == cont.end() && s.length() < 24;
    }
};

lines.erase(std::remove_if( lines.begin(), lines.end(), boost::bind<bool>(Discard(), old_samples, _1)), lines.end());
+3  A: 

You need boost::labmda::bind to lambda-ify function calls, for example the length < 24 part becomes:

bind(&string::length, _1) < 24

EDIT

See "Head Geek"'s post for why set::find is tricky. He got it to resolve the correct set::find overload (so I copied that part), but he missed an essential boost::ref() -- which is why the comparison with end() always failed (the container was copied).

int main()
{
  vector<string> strings = getstrings();
  set<string> others = getothers();
  set<string>::const_iterator (set<string>::*findFn)(const std::string&) const = &set<string>::find;
  strings.erase(
    remove_if(strings.begin(), strings.end(),
        bind(&string::length, _1) < 24 &&
        bind(findFn, boost::ref(others), _1) == others.end()
      ), strings.end());
  copy(strings.begin(), strings.end(), ostream_iterator<string>(cout, ", "));
  return 0;
}
Adam Mitz
D'oh! Of course... thanks for the hint, I'll add that to mine too.
Head Geek
+2  A: 

The main problem, other than the bind calls (Adam Mitz was correct on that part), is that std::set<std::string>::find is an overloaded function, so you can't specify it directly in the bind call. You need to tell the compiler which find to use, like so:

using namespace boost::lambda;
typedef std::vector<std::string> T1;
typedef std::set<std::string> T2;

T1 strings = getstrings();
T2 others = getothers();

T2::const_iterator (T2::*findFn)(const std::string&) const=&T2::find;
T2::const_iterator othersEnd=others.end();

strings.erase(std::remove_if(strings.begin(), strings.end(),
 (bind(&std::string::length, _1) < 24
  && bind(findFn, boost::ref(others), _1) == othersEnd)),
 strings.end());

This compiles, but it doesn't work properly, for reasons I haven't yet figured out... the find function is never returning others.end(), so it's never deleting anything. Still working on that part.

EDIT: Correction, the find function is returning others.end(), but the comparison isn't recognizing it. I don't know why.

LATER EDIT: Thanks to Adam's comment, I see what was going wrong, and have corrected the problem. It now works as intended.

(Look at the edit history if you want to see my full test program.)

Head Geek
It's always a ref or a var...
Adam Mitz