Boost phoenix or lambda library problem: removing elements from a std::vector
-
03-07-2019 - |
Question
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());
Solution
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;
}
OTHER TIPS
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.)