combine boost unordered_map, bind, and std::find_if - c++

I have a big container of boost::unordered_map like below:
typedef boost::unordered_map< vertex, entity > vertex_container;
The vertex class has a coordinate member variable representing its location.
I have several coordinate point_(s). I would like search if any vertex inside my container exist so that vertex.coordinate = point.
something like:
vertex_container::iterator it = std::find_if(v_container.begin(), v_container.end(), boost::bind(&Vertex::coordinate(), _1) == point);
but it fails.
I tried:
vertex_container::iterator it = std::find_if(v_container | boost::adaptors::map_keys(boost::bind(&vertex::coordinate(), _1)) == point);
error: cannot call member function ‘mesh::coordinate mesh::Vertex::coordinate() const’ without object.
I somehow try to combine boost unordered_map, bind, and std::find_if.
Please NOTE I am only able to use C++09 standard and boost 1.53.0 version.

what you need to do is to first bind the key out of the unordered_map and then bind again the member function.
vertex_container::iterator it = std::find_if( v_container.begin(), v_container.end(), (boost::bind(&vertex::coordinate, (boost::bind( &vertex_container::value_type::first, _1))) == point) );
and also you cannot use pipe within std::find_if.

In your code you have :
boost::bind(&Vertex::coordinate, _1) == point
this is a comparison between boost::bind(...) and point, and, since it is a comparison, it is a boolean value. Or more probably, your compiler doesn't know how to compare the two.
std::find_if takes a function returning boolean as argument.
This means, since you're on <c++11 you'll have to declare a function somewhere like:
bool isVertexEqualToPoint(Vertex*, point){
return vertex.coordinate==point;
}
and then you can boost::bind that function to compare against your point.
I do think however that is is more elegant here to make a compare object. Have a look at this question. It should be straightforward. Just replace the somehow_compare with your own condition.

Related

How to implement something like std::copy_if but apply a function before inserting into a different container

Full disclosure, this may be a hammer and nail situation trying to use STL algorithms when none are needed. I have seen a reappearing pattern in some C++14 code I am working with. We have a container that we iterate through, and if the current element matches some condition, then we copy one of the elements fields to another container.
The pattern is something like:
for (auto it = std::begin(foo); it!=std::end(foo); ++it){
auto x = it->Some_member;
// Note, the check usually uses the field would add to the new container.
if(f(x) && g(x)){
bar.emplace_back(x);
}
}
The idea is almost an accumulate where the function being applied does not always return a value. I can only think of a solutions that either
Require a function for accessing the member your want to accumulate and another function for checking the condition. i.e How to combine std::copy_if and std::transform?
Are worse then the thing I want to replace.
Is this even a good idea?
A quite general solution to your issue would be the following (working example):
#include <iostream>
#include <vector>
using namespace std;
template<typename It, typename MemberType, typename Cond, typename Do>
void process_filtered(It begin, It end, MemberType iterator_traits<It>::value_type::*ptr, Cond condition, Do process)
{
for(It it = begin; it != end; ++it)
{
if(condition((*it).*ptr))
{
process((*it).*ptr);
}
}
}
struct Data
{
int x;
int y;
};
int main()
{
// thanks to iterator_traits, vector could also be an array;
// kudos to #Yakk-AdamNevraumont
vector<Data> lines{{1,2},{4,3},{5,6}};
// filter even numbers from Data::x and output them
process_filtered(std::begin(lines), std::end(lines), &Data::x, [](int n){return n % 2 == 0;}, [](int n){cout << n;});
// output is 4, the only x value that is even
return 0;
}
It does not use STL, that is right, but you merely pass an iterator pair, the member to lookup and two lambdas/functions to it that will first filter and second use the filtered output, respectively.
I like your general solutions but here you do not need to have a lambda that extracts the corresponding attribute.
Clearly, the code can be refined to work with const_iterator but for a general idea, I think, it should be helpful. You could also extend it to have a member function that returns a member attribute instead of a direct member attribute pointer, if you'd like to use this method for encapsulated classes.
Sure. There are a bunch of approaches.
Find a library with transform_if, like boost.
Find a library with transform_range, which takes a transformation and range or container and returns a range with the value transformed. Compose this with copy_if.
Find a library with filter_range like the above. Now, use std::transform with your filtered range.
Find one with both, and compose filtering and transforming in the appropriate order. Now your problem is just copying (std::copy or whatever).
Write your own back-inserter wrapper that transforms while inserting. Use that with std::copy_if.
Write your own range adapters, like 2 3 and/or 4.
Write transform_if.

Insert new pair in std::map<CString, CString>

I have two lists and a name to find. If the name to find is not in the first list then it might be in the second list with a slightly different format. Conversion function between the two formats are given.
std::map<CString, CString>* convertedNames;
BOOL CSome::SeekNameWithConversion(std::set<CString> names, CString nameToFind)
{
for (auto it = names.begin(); it != names.end(); ++it)
{
if (nameToFind.Compare(*it) == 0) return true;
auto convertedIt = convertedNames->find(*it);
if (convertedIt != convertedNames->end() &&
nameToFind.Compare(convertedIt->second) == 0)
return true;
CString justConvertedName = ConvertToTheOtherFormat(nameToFind);
convertedNames->insert(*it, justConvertedName); // Error here
return nameToFind.Compare(justConvertedName) == 0;
}
}
The error which appears is:
error C2675: unary '++':
'ATL::CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<_CharType>>>' does
not define this operator or a conversion to a type acceptable to the
predefined operator
I would like to know why the operator ++ is involved here and then how should I treat this error.
Most of the various insert functions of std::map require an iterator. Instead, you pass the pointed-to-object, (which is a CString, I suppose):
convertedNames->insert(*it, justConvertedName);
^^^
this is a CString, not a std::map<CString,CString>::iterator
If you want to insert a key-value pair, use the map's value_type instead which is basically a std::pair made up of key and value:
convertedNames->insert(std::make_pair(*it, justConvertedName));
The first argument to map::insert is an iterator, not a CString. Internally, the method is trying to increment the iterator. This apparently makes a call to operator++. You don't need to use this insert overload. It's intended to improve performance when you know a position close to where the element will be inserted. Just call convertedNames->insert(std::make_pair(*it, justConvertedName)) instead.
So the first thing to understand is that templates are always fully implemented in the header, as any classes required are built with that object (just think if the std lib had all possible std::vector implementations built in!)
This means that the implementation of the template is exposed - and in this case, there's a ++ somewhere there. If you take the whole error printed (it'll be quite a few more lines) you may even be told which parameter you've got wrong.
In any case, we can see that *it is clearly going to be a CString, but I'd guess that this
... convert to the other format
is probably not returning what you think

error: expression must be a modifiable lvalue when using find_if

I have two vectors of classes that contain mainly strings, and I'm trying to keep track of how many times there was a match between two vectors. I kept an int counter in one of the two public classes (necessary for another function). However, std::find_if doesn't seem to allow me to modify nor assign this counter variable.
Following is the std::find_if search algorithm:
for (Vector1& v1 : vector1) {
auto res = find_if(vector2.begin(), vector2.end(),
[=](Vector2 v2) {
if (v2.code == v1.code) {
v1.counter++; // <-- where the error occurs
return true;
}
else
return false;
}
);
}
I can't seem to figure out why this happens; my speculation is that the third parameter for the find_if algorithm takes in a const value. But that shouldn't affect my vector1, right?
I used nested ranged for-loops instead, and it works perfectly. However, I'd like to try using this find_if algorithm instead...
You have a problem with capture/pass by value/reference.
It should be [&] or [&v1] - variables captured by value are non-mutable by default, and lambda's operator() is const. You could use the mutable keyword to fix the error, which makes operator() non-const, but you wouldn't see the changes made to v1 anyways.
Additionally, you should be passing by Vector2 const& v2, auto const& or auto && in sake of avoiding making a copy.
Together:
[&v1](Vector2 const& v2) { ... }
I'd like to try using this find_if algorithm instead...
But that's not what it's for. If you aren't going to use the returned iterator, don't do it. You should be getting a warning. Use loops for simple iteration.

How do I apply remove_if with a UUID comparison, using Boost lambdas?

I have a list of UUID's. I want to remove elements in the list with a given UUID. My question is, how do I achieve this, using boost lambdas and the std::list::remove_if method, so that I don't have to write a comparison function externally.
If your container is a std::list, then you have the member functions std::list::remove() and std::list::remove_if().
Since boost::Uuid already implements a comparison operator==(), then you can simply do:
std::list<boost::Uuid> uuids;
...
uuids.remove(uuidToRemove);
Boost UUIDs are comparable and provide a comparison operator. You can just use std::remove when you know the UUID.
std::vector<boost::Uuid> uuids = /* ... */;
boost::Uuid toRemove;
uuids.erase( std::remove(uuids.begin(), uuids.end(), toRemove), uuids.end() );
If you want to, you could use boost bind to fix one argument
bool cmp(boost::Uuid const& x, boost::Uuid const& y) {return x == y;}
uuids.erase( std::remove_if(uuids.begin(),
uuids.end(),
boost::bind(cmp, toRemove, _2) )
uuids.end() );

remove_if string matches a given string in a set

I was thinking about using remove_if on a vector of strings as follows in the pseudo code below:
for(some strings in a given set) {
remove_if(myvec.begin(), myvec.end(), string_matches_current_string);
}
Now, I am aware I can define the predicate and make this work easily. But I was wondering if there is a standard template function that I could use in place of the predicate above to make this work. I was looking around and couldn't find one. Appreciate any ideas with examples. Thanks!
Why using std::remove_if if you already know the value you want to remove? Use std::remove, which removes the items in the provided range, that match the given value:
std::vector<std::string>::iterator new_end = my_vec.end();
for(const auto &current_set_string : some_set)
new_end = std::remove(myvec.begin(), new_end, current_set_string);
my_vec.erase(new_end, my_vec.end()); // effectively remove them from the vector.
Note that I used the range-based for loop just to make this shorter, but you should use a regular loop if you can't use C++11.
I'm pretty sure there isn't a standard function but you can easily write the whole expression using a C++11 lambda:
std::remove_if(myvec.begin(), myvec.end(),
[&compare_me](std::string const& cmp) -> bool
{
return compare_me == cmp;
});
With compare_me being the "current" string set by the outer loop.
Keep in mind that remove_if returns an iterator to one past the last valid element so in order to get the correct myvec, you have to erase the elements between the iterator returned by remove_if and myvec.end().
For a non-C++11 implementation you'd have to turn the lambda into a function or functor. If you turn it into a functor you can pass the functor directly, if you turn it into a function you'll have to use something like boost::bind to provide the necessary glue.