stl find_if and case-insensitive string comparison

boost, c++, c++03, string

Solution

Calling, rather than binding, a function

boost::iequals(boost::bind(&Model::mName, _1), assetName)

The bind is a red herring: this is currently not a functor, but a function call.

You're (accidentally) trying to call it immediately, and use the boolean result as the comparison function for `std::find_if`. Naturally, that's not correct.

You were right to bind the model name, but you'll still have to bind the actual call to `iequals`, too.

Here's a prior example on the Boost users' mailing list - first Google result for `bind iequals`.

Try something like:

boost::bind(
   &boost::iequals<std::string,std::string>,
   boost::bind(&Model::mName, _1), // 1st arg to iequals
   assetName,                      // 2nd arg to iequals
   std::locale()                   // 3rd arg to iequals
)

Notice that template argument deduction is not possible here; also notice that we have to provide `boost::iequals`'s default third argument explicitly, because defaults don't provide us the magic of being able to omit arguments when binding functions.

Full working testcase:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <boost/bind.hpp>
#include <boost/algorithm/string/predicate.hpp>

struct Model
{
    Model(const std::string& name) : name(name) {};
    std::string mName() const
    {
        return name;
    }

private:
    std::string name;
};

int main()
{
    std::vector<Model> mModels;
    mModels.push_back(Model("a"));
    mModels.push_back(Model("b"));
    mModels.push_back(Model("c"));

    const std::string assetName = "B";

    std::vector<Model>::iterator it = std::find_if(
        mModels.begin(),
        mModels.end(),
        boost::bind(
            &boost::iequals<std::string,std::string>,
            boost::bind(&Model::mName, _1),
            assetName,
            std::locale()
        )
    );

    assert(it != mModels.end());
    std::cout << it->mName() << std::endl; // expected: "b"
}

(See it working live here.)

Why did my original code work, then?

In `boost::bind(&Model::mName, _1) == assetName`, the operator `==` is overloaded when used with `boost::bind` to do magic; although it looks like you're doing a direct comparison, the comparison is not actually evaluated in the argument to `std::find_if`, but deferred until later (essentially by performing an implicit `bind` that you can't see).

In the case of a normal function call, though, such as to `boost::iequals`, we have to apply that "magic" ourselves, which is what the above is all about.

Problem

I have a vector of `Models` like below: ``` struct Model { std::string mName; // ....... }; ``` Given a string representing a model name, I want to see if I can find one of the models in the vector. Right now I have this: ``` std::string assetName = "monkey"; std::vector<Model>::iterator iter = std::find_if(mModels.begin(), mModels.end(), boost::bind(&Model::mName, _1) == assetName); ``` But this does not do case insensitive string comparison. So I read about `boost/algorithm/string.hpp`, with `boost::iequals`, that does this correctly. Here's my attempt at using it: ``` std::vector<Model>::iterator iter = std::find_if(mModels.begin(), mModels.end(), boost::iequals(boost::bind(&Model::mName, _1), assetName)); ``` Yet this does not compile and reports some hundred lines of compilation errors. I believe std::find_if expects a third param function with only 1 parameter. is there a simple fix for this? EDIT: I forgot to mention I cannot use C++11, but I can use boost! EDIT2: The answer below seems to give me this compilation error, using this: ``` std::vector<Model>::iterator iter = std::find_if(mModels.begin(), mModels.end(), boost::bind(&boost::iequals<std::string, std::string>, boost::bind(&Model::mName, _1), assetName)); bind.hpp(69): error C2825: 'F': must be a class or namespace when followed by '::' 2> bind\bind_template.hpp(15) : see reference to class template instantiation 'boost::_bi::result_traits<R,F>' being compiled 2> with 2> [ 2> R=boost::_bi::unspecified, 2> F=bool (__cdecl *)(const std::string &,const std::string &,const std::locale &) 2> ] 2> resourcemanifest.cpp(24) : see reference to class template instantiation 'boost::_bi::bind_t<R,F,L>' being compiled 2> with 2> [ 2> R=boost::_bi::unspecified, 2> F=bool (__cdecl *)(const std::string &,const std::string &,const std::locale &), 2> L=boost::_bi::list2<boost::_bi::bind_t<const std::basic_string<char,std::char_traits<char>,std::allocator<char>> &,boost::_mfi::dm<std::string,Model>,boost::_bi::list1<boost::arg<1>>>,boost::_bi::value<std::string>> 2> ] ```

Original source