views:

815

answers:

4

I need to designe predicate for stl algorithms such as find_if, count_if.

namespace lib
{
    struct Finder
    {
        Finder( const std::string& name ):
            name_( name )
        {
        }

        template< typename TElement >
        bool operator( const TElement& element )
        {
            return element.isPresent( name_ );
        }

        /* template< typename TElement >
        bool operator( const TElement& element )
        {
            const Data& data = element.getData();
            return data.isPresent( name_ );
        }*/ 
    };
}

But I need it to have different operators () according to presence of some certain methods in TElement. Like if it has "getData" I'd like to check that data and if it hasn't I'd do some other actions.

I am aware of SFINAE. But I don't have boost:: on the project. So either there is some easy implementation of template "has_method" or you know some other design solution.

I can't point specific types and simply overload because I'd like to put this Predicate to the one of the project library, which don't know about those specific classes with "getData" method.

Solution with class traits are good as far as there is no namespaces. Predicate Finder in in "lib" namespace and class with "getData" is in "program" namespace.

Thanks.

+1  A: 

You can have a look at Veldhuizen's homepage for the switch template. You can probably use this to choose the exact operator?

dirkgently
std::find_if have to choose correct one.
Mykola Golubyev
A: 

Have your types derive from "functionality types" (eg a type "has_function1") that will work as java interfaces and you have a chance, because SFINAE can be used to test if one type can be converted into another one.

If you're interested, i can look into it and give you a more detailed answer.

EDIT : I know you said you didn't have Boost libraries available, but is there anything preventing you from getting the few files that are needed to get boost::is_convertible working ? There would not be anything in particular to compile !

Benoît
Those different types are not in relations. They have something in common in their logic and meaning but they are not in the hierarchy.
Mykola Golubyev
+3  A: 

Why use template mathods at all? Just use the specific class that you want to base it on or a common base classes if there are lots of class types.

e.g.

struct Finder
{
    Finder( const std::string& name ):
        name_( name )
    {
    }

    bool operator( const IsPresentBaseClass& element )
    {
        return element.isPresent( name_ );
    }

    bool operator( const GetDataBaseClass& element )
    {
        const Data& data = element.getData();
        return data.isPresent( name_ );
    } 
 };

If this pattern happens a lot with different class types and you know the types before using the predicate you could template the predicate itself.

e.g.

template<class T1, class T2>
struct Finder
{
    Finder( const std::string& name ):
        name_( name )
    {
    }

    bool operator( const T1& element )
    {
        return element.isPresent( name_ );
    }

    bool operator( const T2& element )
    {
        const Data& data = element.getData();
        return data.isPresent( name_ );
    } 
 };

Or another approach you could use is to use some sort of class traits to hold the information.

e.g.

struct UseIsPresent
{
    template<class T>
    static bool CompareElement( const T& element, const std::string& name )
    {
        return element.isPresent( name );
    }
};

struct UseGetData
{
    template<class T>
    static bool CompareElement( const T& element, const std::string& name )
    {
        const Data& data = element.getData();
        return data.isPresent( name );
    } 
};

// default to using the isPresent method
template <class T>
struct FinderTraits
{
    typedef UseIsPresent FinderMethodType;
};

// either list the classes that use GetData method
// or use a common base class type, e.g. UseGetData
template <>
struct FinderTraits<UseGetData>
{
    typedef UseGetData FinderMethodType;
};

struct Finder
{
    Finder( const std::string& name )
    : name_( name )
    {
    }

    template<class T>
    bool operator()( const T& element )
    {
        return FinderTraits<T>::FinderMethodType::CompareElement<T>(element, name_);
    }

    std::string name_;
};

The downsides of all these methods is that at some point you need to know the types to be able to split them up into which method to use.

Shane Powell
Yes. I forgot to point this problem. Please read edit section in a moment.
Mykola Golubyev
Do you know the specific types at the use of the Predicate? You could template the while Finder struct on those classes? It just seems you need specify concrete types a some point.
Shane Powell
If you really don't know the types, you could use something like class traits to figure it out. It does require you to setup traits for all the classes involved tho, or at least common base class types.
Shane Powell
You mean kind of "bool IsPresent<TElement>::isPresent( name )"?
Mykola Golubyev
I've updated the answer with ways I would think about solving the problem. See if any fit your liking. Nothing is perfect tho. In the end you need to somehow split the finder out by type, it's how much management overhead you want to incur.
Shane Powell
I've updated question. Traits are good, but I can't put traits neither to "lib" namespace nor to "program".
Mykola Golubyev
A: 

Boost is not magic; using SFINAE is fairly straightforward:

    template< typename TElement >
    bool operator( const TElement& element, ... )
    {
        return element.isPresent( name_ );
    }

    template< typename TElement >
    bool operator( const TElement& element, const Data& data = element.getData())
    {
        return data.isPresent( name_ );
    }

SFINAE will remove the second overload if it doesn't compile. Overload resolution will pick the second, if it does compile since ... is a worse match.

MSalters
I wish it works. But it won't even compile.
Mykola Golubyev