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



Comments

  • Whats the LONG way around doing that.

    Posted by Legacy on 11/29/2000 12:00am

    Originally posted by: aaron Yemm

    I need to do exactly that for one of my projects. But i have made sure that the pointer to the function knows what object it belongs to just by putting the (ClassName::*pFunc) in wherever it was required.
    now when i try to invoke the function in the view as in pDoc->pFunc(params); it states that the expression does not evaluate to a function. I have made a function which gets a pFunc passed to it so that i dont have to directly call the pointer function in the view. But i get the same error. It should work either way I suppose. I know I dont need the function which has a pFunc passed to it. But i thought it may fix it. Have a quick fix?

    :(
    boohoo

    Reply
  • how would you approach callback functions?

    Posted by Legacy on 02/14/1999 12:00am

    Originally posted by: Joe Shapiro

    I've often wondered how I might pass a call back function pointer to qsort, for example, that is actually a non-static member function.

    Do you have any thoughts on this topic?

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: August 20, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT When you look at natural user interfaces as a developer, it isn't just fun and games. There are some very serious, real-world usage models of how things can help make the world a better place – things like Intel® RealSense™ technology. Check out this upcoming eSeminar and join the panel of experts, both from inside and outside of Intel, as they discuss how natural user interfaces will likely be getting adopted in a wide variety …

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds