Function Pointers to Non-Static Object Methods

One of the first things that I noticed when learning C++, was that function pointers
had become much more difficult to use with object architecture. Each function/method
needs to know what object it is part of.  So, to point to an object’s method, we also
must know about an object. Using the data from each, we can freely call any method of any
object, without knowing exactly what we are calling.

The code I have included is a holder class that takes a pointer to an object and a
pointer to a method.  Either can be changed at any time. During the lifetime of the
holder class object, a helper method can be called that calls the stored method pointer.
  Its that simple…Sort-of.  The syntax can get a little confusing, but I’ll
try to walk you through.

First, an abstract base class is defined.  The CMethodPtr class is derived from
this class, but this class is used to pass the data around between functions. You’ll see
what I mean later.

// R is the return type
template <class R>
class CMethodPtrBase
{
public:
    virtual R CallMethod() = 0;
    virtual R CallMethod(LPVOID arg1) = 0;
    virtual R CallMethod(LPVOID arg1, LPVOID arg2) = 0;
};

Next is the derived class.  Both the abstract base class and the derived class
require the return type of the function being held.  The derived class requires
another template argument declaring the type of object this holder will store. Neither of
these can be changed once the holder has been declared.

The constructor takes a pointer to the actual object( not the definition like above)
that contains the method we will call. It also takes a pointer to the method.  The
METHOD macro is used to easily pass the method into the holder.

The CallMethod() methods are the helper methods for calling the method that the holder
object contains.  The different versions are for different numbers of arguments.
  If more arguments are needed, then add new functions to the abstract base class and
to this class.

// r = return type
// o = object definition
// m = method
#define METHOD(r,o,m) (r (o::*)() )(&o::m)

// O = object definition
// R = return type
template <class O, class R>
class CMethodPtr : public CMethodPtrBase<R>
{
    typedef R (O::*PMETHOD)();
    typedef R (O::*PMETHOD1)(LPVOID);
    typedef R (O::*PMETHOD2)(LPVOID, LPVOID);

public:
    CMethodPtr(O* pObject, PMETHOD pMethod) : m_pObject( pObject ),
m_pMethod( pMethod ) {};

    // Member Variables
private:
    O*    m_pObject;    // pointer to an
object
    PMETHOD    m_pMethod;    // pointer to
the method

    //Implementation
public:
    void SetObject( O* pObject ) { m_pObject = pObject; }
    void SetMethod( PMETHOD pMethod ) { m_pMethod = pMethod; }

    // 0 args
    virtual R CallMethod()
    {
        return (m_pObject->*m_pMethod)();
    }

    // 1 arg
    virtual R CallMethod(LPVOID arg1)
    {
        return
(m_pObject->*(PMETHOD1)m_pMethod)(arg1);
    }

    // 2 args
    virtual R CallMethod(LPVOID arg1, LPVOID arg2)
    {
        return
(m_pObject->*(PMETHOD2)m_pMethod)(arg1, arg2);
    }
};

That is basically it for the definition.  Here is how it is used:

Somewhere declare the object that has the method we want to point to and the holder
object:

// A helper macro that uses the METHOD macro
#define SUMMETHOD(m) METHOD(int, CSumObject, m)

CSumObject m_SumObject;   // The object that has the method we want to point
to
CMethodPtr<CSumObject, int> m_SumMethodPtr( &m_SumObject, SUMMETHOD(Function2)
); // Uses a CSumObject thats returns an int

Function2() is the method that will be held. Later on we can call that function like
this:

int nArg1 = 5;
int nArg2 = 10;

int nReturn;

nReturn = m_SumMethod.CallMethod( &nArg1, &nArg2 );  // Whatever it was
pointing to, we just called it

If you want to pass a holder object through a function, define the function this way:

int MyObject::SomeFunction( CMethodPtrBase<int> pMethodPtr )
{
    return pMethodPtr->CallMethod();
}

Notice the abstract base class is used so that we do not have to know what type of
class the CMethodPtr object is holding.  If we used CMethodPtr directly, the template
arguments would require us to tell it what type of class the CMethodPtr object was
holding.

Take a look at the demo project or source to see a more thorough example.

Download demo project – 15.8 KB

Download source – 7.7 KB

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read