Posts Tagged ‘template’

The Problem

Suppose you need to use a legacy C++ class designed in COM style. Some methods of the class return HRESULT, and take a pointer as a parameter that could point to an int, a short, a std::string, an enum, and so on. For example:

HRESULT GetName(std::string* name);

Solution 1

The most obvious way of calling this method is:

std::string name;
if (SUCCEEDED(obj->GetName(&name)))
    m_name = name;
    m_name = "Untitled";

If you call the methods over and over again using this way, you will create quite a bit of duplicated code.

Solution 2

One way to solve this is to provide a helper function for the methods that have the same parameter type. For example, for the methods that take int* as the parameter, you could write a helper function, something like follows:

int GetInt(MyClass* obj, HRESULT (MyClass::*func)(int*), int defaultValue)
    int i = defaultValue;

    if (SUCCEEDED(obj->*func(&i))) // call the member method
        return i;
        return defaultValue;

The obj parameter points to a MyClass object on which the member method is to be called; the func parameter points to a member method of MyClass to be called; the defaultValue parameter is to be returned if the function call to the member method fails. The helper function hides the details of checking the HRESULT returned from the member method.

You can now use the helper function when you need to call a method on a MyClass object, e.g.:

int length = GetInt(obj, &MyClass::GetLength, 0);

The code for calling the GetName() method can be reduced to:

m_name = GetString(obj, &MyClass::GetName, "Untitled");

The code becomes much more concise. However, there is a problem: you need to write a helper function for each group of methods of each class, where each group of methods has the same parameter type. Let’s say, you have five classes and all of them have methods that use 10 different parameter types, then you have to create 50 helper functions, which is quite a bit of (seemingly duplicated) code.

A Better Solution

Is there a better solution? Yes, by using a combination of template and member function pointers. We can define a template Functor class PropertyGetter:

// A Functor for calling member methods that returns HRESULT and takes a
// pointer as the parameter, e.g. HRESULT MyClass::GetLength(int* length)
// T represents a class whose method is to be called.
// ARG represents the type of the parameter of the method to be called.
template<typename T, typename ARG>
class PropertyGetter
    T* m_object; // The object on which the method is to be called
    HRESULT (T::*m_func)(ARG*); // The member method to be called

    PropertyGetter(T* obj, HRESULT (T::*func)(ARG*))
      : m_object(obj), m_func(func)

    ARG operator()(ARG defaultValue)
        ARG a = defaultValue;
        if (SUCCEEDED((m_object->*m_func)(&a))) // call the member method
            return a;
            return defaultValue;

The template parameter T represents a class whose methods you want to call, and ARG represents the type of the parameter of a method. The constructor takes two parameters: a pointer to a class instance and a pointer to a member method. The operator() calls the target method on the class instance. If the call succeeds, it returns the value returned from the method; otherwise, it returns the defaultValue passed in.

With this template class, you can call any method of any class, as long as the method returns HRESULT and takes a pointer as the parameter. The GetName() and GetLength() methods can now be called as:

m_name = PropertyGetter<MyClass, std::string>(obj,

int length = PropertyGetter<MyClass, int>(obj, &amp;MyClass::GetLength)(0);

which can be conceptually translated into:

    PropertyGetter<MyClass, std::string> temp1(obj, &aMyClass::GetName);
    m_name = temp1("Untitled");

int length;
    PropertyGetter<MyClass, int> temp2(obj, &MyClass:GetLength);
    length = temp2(0);

As you can see in this example, when used properly, the combination of template and function pointers can help you make your code more concise.


Read Full Post »