Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass predicate as function parameter

I have a class CMyVector which holds a vector of pointers to CMyClass objects and I have several "find" functions to find elements according to differente criteria. So for example, I have:

CMyClass* CMyVector::FindByX(int X);
CMyClass* CMyVector::FindByString(const CString& str);
CMyClass* CMyVector::FindBySomeOtherClass(CSomeOtherClass* ptr);
// Other find functions...

At first, they were implemented as loops, traversing the vector, looking for the element that matches X, str, ptr or whatever. So I've created predicates, like this one:

class IsSameX:public unary_function<CMyClass*, bool>
{
    int num;
public:
    IsSameX(int n):num(n){}
    bool operator()(CMyClass* obj) const 
    { 
        return (obj != NULL && (obj->X() == num)); 
    }
};

And ended with a bunch of functions which all look like this:

CMyClass* CMyVector::FindByX( int x )
{
    CMyVector::iterator it = find_if(vec.begin(), vec.end(), IsSameX(x));
    if (it != vec.end())
    {
        return *it;
    }
    return NULL;
}

They all look the same, except for the predicate that is called, so I've thought of simplifying more, and created a function like this one:

CMyClass* CMyVector::Find( ThisIsWhatIDontKnow Predicate)
{
    CMyVector::iterator it = find_if(vec.begin(), vec.end(), Predicate);
    if (it != vec.end())
    {
        return *it;
    }
    return NULL;
}

And do:

CMyClass* CMyVector::FindByX( int x )
{
    return Find(IsSameX(x));
}

And so on.

So my question is: How should I declare my Find function so I can pass it my predicates? I've tried several ways, but with no luck so far.

like image 693
MikMik Avatar asked Dec 05 '11 15:12

MikMik


1 Answers

use template to take in whatever ever type you need

template<typename UnaryPredicate>
CMyClass* CMyVector::Find(UnaryPredicate Predicate)
{
    CMyVector::iterator it = find_if(vec.begin(), vec.end(), Predicate);
    if (it != vec.end())
    {
        return *it;
    }
    return NULL;
}

You could alway also use std::function (c++11)

CMyClass* CMyVector::Find(std::function<bool(const (CMYClass*)&)> Predicate)
{
    CMyVector::iterator it = find_if(vec.begin(), vec.end(), Predicate);
    if (it != vec.end())
    {
        return *it;
    }
    return NULL;
}

Personally I prefer the top one, because it will probably be easier for the compiler to optimize as there is less indirection. and if the call is unique it might get inlined.

EDIT: It is also worth noting that if go you with the templated option, you will have to provide the implementation in the header file, this can be a pain. Whereas the std::function can live in the source (.cpp) file with all the other implementations.

like image 185
111111 Avatar answered Oct 21 '22 13:10

111111