cool thing with memberfunction pointers as parameters

P

PKH

This is a technique that was used in an object-oriented program-architecture
in C that I worked with in the past, and I didn't think was possible to do
something similar for classes in C++. Basically it is a way to send virtual
functions as function-parameters, and is useful in cases where you f.ex have
a class that manages other objects and want to call different functions on
these objects without having to rewrite the traversal code for each call.
This may be obvious to many, but I thought it was cool that it could be done
and maybe someone else will find a use for it also.
The drawback is that you need to use a void* to pass any parameters to the
functions.

here is a simple testprogram to show it:


class BaseClass; // foward declaration of class

typedef void (BaseClass::* BaseClassFunc)(void*); // typedef a
functionpointer for

// BaseClass member function.

// The void* is not used here, but

// is meant for any data that would

// need to be passed
class BaseClass
{
protected:
int
m_int;

public:
BaseClass(){m_int = 0;}
virtual void VirtualFunc(void* pxContext) = 0; // just some meaningless
testfunction
void CallVirtual(BaseClassFunc pF, void*
pxContext){(this->*pF)(pxContext);} // call the parameter-function on self
};

class DerivedClass : public BaseClass
{
public:
void VirtualFunc(void* pxContext){m_int = 1;} // override the VirtualFunc
from the BaseClass
};

int main(int argc, char* argv[])
{

DerivedClass
D;

void*
pxContext = NULL;

/* the following actually calls DerivedClass::VirtualFunc on D, as you
can see by stepping into it. */
/* This function would normally be made for some other class, and contain
the traversal-code */
/* but in this example it is just a member of BaseClass */
D.CallVirtual(BaseClass::VirtualFunc, pxContext);

return 0;
}
 
M

Michiel Salters

PKH said:
class BaseClass; // foward declaration of class

typedef void (BaseClass::* BaseClassFunc)(void*); // typedef a
functionpointer for

// BaseClass member function.

// The void* is not used here, but

// is meant for any data that would

// need to be passed
class BaseClass
{
protected:
int
m_int;

public:
BaseClass(){m_int = 0;}
virtual void VirtualFunc(void* pxContext) = 0; // just some meaningless
testfunction
void CallVirtual(BaseClassFunc pF, void*
pxContext){(this->*pF)(pxContext);} // call the parameter-function on self
};

class DerivedClass : public BaseClass
{
public:
void VirtualFunc(void* pxContext){m_int = 1;} // override the VirtualFunc
from the BaseClass
};

int main(int argc, char* argv[])
{

DerivedClass
D;

void*
pxContext = NULL;

/* the following actually calls DerivedClass::VirtualFunc on D, as you
can see by stepping into it. */
/* This function would normally be made for some other class, and contain
the traversal-code */
/* but in this example it is just a member of BaseClass */
D.CallVirtual(BaseClass::VirtualFunc, pxContext);

return 0;
}

Actually, you must say &BaseClass::VirtualFunc. Your compiler failed
to warn you, so you might want to check if it's warning level is set
too low.

Besides, it's needlessly complex. You can always say
(D.*(&BaseClass::VirtualFunc))(pxContext).

The function CallVirtual does not add any functionality, it only saves
on parentheses.

With boost::bind, you can do even fancier things:

boost::bind( &BaseClass::VirtualFunc, _1, pxContext )( D ).

The boost::bind call creates a unary functor, where the single
argument _1 is substituted on every call. The second parameter
needed for BaseClass::VirtualFunc, pxContext is kept.
The advantage is of course that you can use the same same
context for every call to the created functor. This means
you can pass it to std::for_each, and have for_each provide
the different BaseClass/DerivedClass objects.

Regards,
Michiel Salters
 
P

PKH

Michiel Salters said:
"PKH" <[email protected]> wrote in message
class BaseClass; // foward declaration of class

typedef void (BaseClass::* BaseClassFunc)(void*); // typedef a
functionpointer for

// BaseClass member function.

// The void* is not used here, but

// is meant for any data that would

// need to be passed
class BaseClass
{
protected:
int
m_int;

public:
BaseClass(){m_int = 0;}
virtual void VirtualFunc(void* pxContext) = 0; // just some meaningless
testfunction
void CallVirtual(BaseClassFunc pF, void*
pxContext){(this->*pF)(pxContext);} // call the parameter-function on self
};

class DerivedClass : public BaseClass
{
public:
void VirtualFunc(void* pxContext){m_int = 1;} // override the VirtualFunc
from the BaseClass
};

int main(int argc, char* argv[])
{

DerivedClass
D;

void*
pxContext = NULL;

/* the following actually calls DerivedClass::VirtualFunc on D, as you
can see by stepping into it. */
/* This function would normally be made for some other class, and contain
the traversal-code */
/* but in this example it is just a member of BaseClass */
D.CallVirtual(BaseClass::VirtualFunc, pxContext);

return 0;
}

Actually, you must say &BaseClass::VirtualFunc. Your compiler failed
to warn you, so you might want to check if it's warning level is set
too low.

I don't think the & is needed. It compiles and runs fine. I've never used &
when taking the address of functions, same as with the address of arrays.
Besides, it's needlessly complex. You can always say
(D.*(&BaseClass::VirtualFunc))(pxContext).

The function CallVirtual does not add any functionality, it only saves
on parentheses.

The point is not to call VirtualFunc directly, but to be able to send a
virtual function as a parameter and have that parameter-function called on
one or more objects. The idea is that you can pass different virtual
functions as the parameter, without rewriting the calling-code. In this
simple example there is no benefit, but if you want to f.ex. traverse some
data-structure and call different virtual functions (passed as the
parameter) on each of the items contained in the data-structure, you would
only need to write the traversal code once.
With boost::bind, you can do even fancier things:

boost::bind( &BaseClass::VirtualFunc, _1, pxContext )( D ).

The boost::bind call creates a unary functor, where the single
argument _1 is substituted on every call. The second parameter
needed for BaseClass::VirtualFunc, pxContext is kept.
The advantage is of course that you can use the same same
context for every call to the created functor. This means
you can pass it to std::for_each, and have for_each provide
the different BaseClass/DerivedClass objects.

Regards,
Michiel Salters

I'm not very familiar with the advanced things you can do with STL, but this
seems to be what I wanted to do (for STL containers).


Pål K Holmberg
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top