views:

107

answers:

2

I'm building a series of predicates that duplicate lots of code, and so are being changed into a single template function class based on the std::unary_function. The idea is that my class interface requires methods such as Element_t Element() and std::string Name() to be defined, so the predicate template arguments are the object type and a value type to which comparison will be made as follows:

// generic predicate for comparing an attribute of object pointers to a specified test value
template <class U, typename R>
class mem_fun_eq : public std::unary_function <U*, bool> {
  private:
    typedef R (U::*fn_t)();
    fn_t fn; 
    R val;
  public:
    explicit mem_fun_eq (fn_t f, R& r) : fn(f), val(r) { } 
    bool operator() (U * u) const {
      return (u->*fn)() == val;
    }   
}; 

Thus, if I have:

class Atom {
  public:
    const Element_t& Element() const { return _element; }
    const std::string& Name() const { return _name; }
};

I would like to perform a search on a container of Atoms and check for either the Name or Element equality using my template predicate like so:

typedef std::string (Atom::*fn)() const;

Atom_it it = std::find_if( _atoms.begin(), _atoms.end(), mem_fun_eq <Atom, std::string> ((fn)&Atom::Name, atomname));

but compiling this returns the following error on the std::find_if line:

error: address of overloaded function with no contextual type information

Also, trying to form the same predicate for a check of the Element() as such:

typedef Atom::Element_t& (Atom::*fn)() const;

Atom_it it = std::find_if(_atoms.begin(), _atoms.end(), mem_fun_eq <Atom, Atom::Element_t> ((fn)&Atom::Element, elmt);

creates a different error!

error: no matching function for call to ‘mem_fun_eq<Atom, Atom::Element_t>::mem_fun_eq(Atom::Element_t& (Atom::*)()const, const Atom::Element_t&)’
note: candidates are: mem_fun_eq<U, R>::mem_fun_eq(R (U::*)(), R&) [with U = Atom, R = Atom::Element_t]
note:                 mem_fun_eq<Atom, Atom::Element_t>::mem_fun_eq(const mem_fun_eq<Atom, Atom::Element_t>&)

Firstly, am I reinventing the wheel with this predicate? Is there something in the STL that I've missed that does the same job in a single class? I can always break the predicate down into several more specific ones, but I'm trying to avoid that.

Secondly, can you help me with the compiler errors?

+3  A: 

I don't know of any easy way to do this using the bits provided with the STL. There is probably some clever boost way, using iterator adapters, or boost::lambda, but personally I wouldn't go that way.

Obviously C++0x lambdas will make all this easy.

Your problem is attempting to cast a function like this:

const std::string&(Atom::*)()

into a function like this:

std::string (Atom::*)()

If you replace your typedef R (U::*fn_t)(); with typedef const R& (U::*fn_t)() const; then it should work.

The following avoids this problem and also provides type inference so that you can just write mem_fun_eq(&Atom::Name, atomname). It compiles for me, although I haven't tested it.

template<typename U, typename R, typename S>
class mem_fun_eq_t : public std::unary_function<U const*, bool>
{
private:
    R (U::*fn_)() const;
    S val_;
public:
    mem_fun_eq_t(R (U::*fn )() const, S val) : fn_(fn), val_(val){}
    bool operator()(U * u)
    {
        return (u->*fn_)() == val_;
    }
};

template<typename U, typename R, typename S>
mem_fun_eq_t<U, R, S> mem_fun_eq(R (U::*fn)() const, S val)
{
    return mem_fun_eq_t<U, R, S>(fn, val);
}
Richard Wolf
+2  A: 

Have you thought of trying to mix in a mem_fun_ref or mem_fun object in place of the member function call?

Basically, you call on mem_fun to create an object that accepts two arguments T* and a template argument to the function A if it has one (or void if it doesn't). Hence you combine it like so:

template<typename MemFunc, typename CompareType, typename T>
struct MyPredicate{
    MyPredicate(MemFunc _functionObj, CompareType _value) 
    : m_Value(_value),
    m_Function(_functionObj){}

    bool operator()(const T &_input){
         return m_Value == m_Function(_input);
    }

 private:
    MemFunc m_Function;
    CompareType m_Value;
 };

Edit:

Ok, that's not completely working so why not have:

struct NamePred: binary_function<Atom*,string,bool>{
    bool operator()(Atom *_obj, string _val){
        return _obj->Name() == _val;
    };
};

then use bind2nd

find_if( atoms.begin(), atoms.end(), bind2nd( NamePred, "yo" ) );
wheaties
Yes, just having the type of the function be a template parameter makes most of the pain go away. You should do this.
Richard Wolf
Can the `MemFunc` type be deduced?
Shamster
@Shamster: good point. A wrapper function would be required to achieve the required type deduction, similar to the mem_fun and mem_fun_ref wrapper functions.
Richard Wolf
I'm actually trying to get it to compile with VS2008 but that's exactly the problem I'm running into. It's having issues with resolving the mem_fun return type. Damn, thought this would help. I'll keep working at it.
wheaties