Function Pointers to Non-Static Object Methods

Tuesday Mar 2nd 1999 by Andrew Schafer
Share:

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

Share:
Home
Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved