Click here to Skip to main content
15,860,844 members
Articles / Programming Languages / Visual C++ 9.0
Article

Functor and Member Function Pointer

Rate me:
Please Sign up or sign in to vote.
4.40/5 (7 votes)
12 Jan 2010CPOL2 min read 26.3K   123   12   6
An article on using a C++ template functor and a member function pointer.

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:

C++
HRESULT GetName(std::string* name);

Solution 1

The most obvious way of calling this method is:

C++
std::string name;
if (SUCCEEDED(obj->GetName(&name)))
    m_name = name;
else
    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:

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

    if (SUCCEEDED(obj->*func(&i))) // call the member method
        return i;
    else
        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.:

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

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

C++
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:

C++
///////////////////////////////////////////////////////////////////////////////
// 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

public:
    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;
        }
        else
        {
            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:

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

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

which can be conceptually translated into:

C++
{
    PropertyGetter<MyClass, std::string> temp1(obj, &MyClass::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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead Rockwell Automation
Singapore Singapore
He is a Software Engineer at Rockwell Automation Asia Pacific Business Center, working on RSLogix 5000. Prior to joining Rockwell Automation, he had worked for Sybase for 8 years and was the original architect of the PowerBuilder Native Interface and the PowerBuilder .NET Compiler that can compile PowerBuilder applications to .NET Windows Forms or Web Forms applications. The programming languages he has used or is using intensively include C#, C++, C and 8086 assembly.

Wu XueSong's Blog

Comments and Discussions

 
QuestionWhy not a template function? Pin
Paolo Messina16-Jan-10 5:39
professionalPaolo Messina16-Jan-10 5:39 
AnswerRe: Why not a template function? Pin
Wu Xuesong17-Jan-10 14:08
Wu Xuesong17-Jan-10 14:08 
GeneralRe: Why not a template function? Pin
Lim Bio Liong17-Jan-10 17:28
Lim Bio Liong17-Jan-10 17:28 
Hello XueSong,

I think if we want to specifically use the PropertyGetter class directly as the functor of an algorithm that iterates over the elements of a container of T objects, we may need to modify the PropertyGetter's constructor to avoid binding to a specific instance of T on construction.

For example, if we have a vector of MyClass objects :

typedef std::vector<MyClass> VECTOR_MyClass;

and we want to iterate this vector, calling the PropertyGetter functor on each iteration, one way to construct the code is follows :

            MyClass c1, c2, c3, c4, c5;
     VECTOR_MyClass vecMyClass;
     std::vector<std::string> vecDefaultString;
     std::vector<std::string> vecOutputString;

     vecMyClass.push_back(c1); vecDefaultString.push_back("str1");
     vecMyClass.push_back(c2); vecDefaultString.push_back("str2");
     vecMyClass.push_back(c3); vecDefaultString.push_back("str3");
     vecMyClass.push_back(c4); vecDefaultString.push_back("str4");
     vecMyClass.push_back(c5); vecDefaultString.push_back("str5");

     std::transform
     (
        vecMyClass.begin(),
        vecMyClass.end(),
        vecDefaultString.begin(),
        std::back_inserter(vecOutputString),
        PropertyGetter<MyClass, std::string>(&MyClass::GetString)
     );

The above usage of PropertyGetter would result in compilation failure because the only PropertyGetter constructor must take in an instance of T (e.g. MyClass).

For this to be possible, I have created a version of PropertyGetter as follows :

#include <functional>

template<typename T, typename ARG>
class PropertyGetter2
     : public PropertyGetter<T, ARG>,
        public std::binary_function<T, ARG, ARG>
{
public:
     PropertyGetter2(HRESULT (T::*func)(ARG*))
          : PropertyGetter(NULL, func)
     {
     }

     ARG operator()(T& obj, ARG defaultValue)
     {
          ARG a = defaultValue;
          if (SUCCEEDED((obj.*m_func)(&a))) // call the member method
          {
               return a;
          }
          else
          {
               return defaultValue;
          }
     }
};

But I also needed to make the PropertyGetter::m_object member a public member in order that PropertyGetter2 is able to access it in its functor.

Any comments, Xue Song ?

- Bio.
GeneralRe: Why not a template function? Pin
Wu Xuesong17-Jan-10 18:11
Wu Xuesong17-Jan-10 18:11 
GeneralRe: Why not a template function? Pin
Lim Bio Liong17-Jan-10 19:11
Lim Bio Liong17-Jan-10 19:11 
GeneralRe: Why not a template function? Pin
Paolo Messina18-Jan-10 21:58
professionalPaolo Messina18-Jan-10 21:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.