Ivan Čukić

Introspection in C++, testing whether a class has a specific method

It is sometimes useful to be able to tell whether a class that the user passes as the template parameter has a specific method, or a specific nested type. (the later is quite heavily used for testing the iterator types in STL).

I’ll demonstrate how you can achieve making a universal immutable std::for_each equivalent for associative containers of STL and Qt. It will receive a collection instead of a pair of iterators to make it a bit more pleasant to use. Like this:

template < typename Container, typename Function >
Function for_each_assoc(const Container & c, Function f)

The problem when creating a function like this is that Qt collection classes, although pretending to support STL API sometimes fail miserably. And this is my /favourite/ case.

In STL, you have cbegin() and cend() methods while in Qt those are called constBegin() and constEnd(). The next issue is that iterating the STL associative containers gives you pairs (std::pair) of keys and values, while in Qt you will be getting an object with methods key() and value(). So, we will have to handle them in a different manner.

namespace details {

// Defining two separate methods for STL and Qt classes.

// The code is using the SFINAE (Substitution Failure Is
// Not An Error) rule.

// The compiler will try to match the nullptr we passed
// to the function against the type of the third argument.
// If it doesn't succeed, it will not report an error,
// it will just try to find the next match.

// This method will be invoked if the container
// has a method named constBegin

template <typename Container, typename Function>
Function _for_each_assoc_helper_container(const Container & c, Function f,
        decltype(&Container::constBegin) * )
{
    return qt_for_each_assoc(c.constBegin(), c.constEnd(), f);
}

// This method will be invoked if the container
// has a method named cbegin

template <typename Container, typename Function>
Function _for_each_assoc_helper_container(const Container & c, Function f,
        decltype(&Container::cbegin) * )
{
    return stl_for_each_assoc(c.cbegin(), c.cend(), f);
}

} // namespace details


// The function receives an associative container c and a functor f.
// It will iterate through the container and call f(key, value)
// for every item in it

template <typename Container, typename Function>
Function for_each_assoc(const Container & c, Function f)
{
    return details::_for_each_assoc_helper_container
        <Container, Function>(c, f, nullptr);
}

Now, we just need to add the functions that do the actual work to the details namespace, and we are set to use our new fun generic algorithm.

template 
Function qt_for_each_assoc(Iterator start, Iterator end, Function f)
{
    for ( ; start != end; ++ start ) f(start.key(), start.value());
    return f;
}

template 
Function stl_for_each_assoc(Iterator start, Iterator end, Function f)
{
    for ( ; start != end; ++ start ) f(start->first, start->second);
    return f;
}

For the actual code, you can check out kde:kactivities/src/utils/for_each_assoc.h

Loading comments...