The callback-to-derived-member function problem

R

ryan.mitchley

Hi all

I have a class (cPort) that is designed to receive objects and,
depending on the type, call a handler (callback) in any descendant of a
cProcessBlock class. Callback functions take a shared_ptr<cBase> as a
parameter, and return void.

The code was working fine, although I have encountered problems (under
a Microsoft compiler, of course - VC 8.0) when I attempt to add
callbacks to a class with multiple inheritance. I hate multiple
inheritance in general, but this is necessary to interface with another
library. I think there is some issue regarding the size of member
function pointers in VC.

Anyway - I believe that this code can be converted to use
boost::function and/or boost::bind to iron out these issues. The
problem is that I am going more than slightly squint when I read the
boost docs.

I have attached a reduced set of my code below. I would be extremely
grateful if someone could point out how this code could be converted to
use the boost representations.

Thanks for any and all replies!

Ryan

typedef void (cProcessBlock::*PortCallbackFnPtr)(shared_ptr<cBase>);

// structure that associates a callback function pointer with the
required caller
class cPortCallback : public cBase
{
public:
cPortCallback() :
m_pPortCallbackFn(NULL), m_pCaller(m_pNULL) { }
cPortCallback(PortCallbackFnPtr pPortCallbackFn,
shared_ptr<cBase> pCaller) :
m_pPortCallbackFn(pPortCallbackFn), m_pCaller(pCaller) { }
PortCallbackFnPtr m_pPortCallbackFn;
shared_ptr<cBase> m_pCaller;
};

class cPort : public cProcessBlock
{
public:
/// Add a new callback
void AddCallback(PortCallbackFnPtr NewCallbackFnPtr, std::string
strClassName,
shared_ptr<cBase> pCaller);

protected:
/// Callback registry (map of callback handlers)
map<string, cPortCallback> m_CallbackRegistry;
};

void cPort::AddCallback(PortCallbackFnPtr NewCallbackFnPtr, std::string
strClassName,
shared_ptr<cBase> pCaller)
{
m_CallbackRegistry[strClassName] = cPortCallback(NewCallbackFnPtr,
pCaller);
}

bool cPort::Timeslice()
// (called repeatedly in thread) - continuously receive new objects and
process using callbacks
{
shared_ptr<cBase> pNewObject;
pNewObject = Receive();

// Call appropriate callback depending on the received object's
class name
if (pNewObject) {
PortCallbackFnPtr CallbackFn =
m_CallbackRegistry[pNewObject->ClassName()].m_pPortCallbackFn;
if (CallbackFn) // if appropriate callback handler is found

((*(shared_polymorphic_cast<cProcessBlock>(m_CallbackRegistry[pNewObject->ClassName()].m_pCaller))).*(CallbackFn))(pNewObject);
}
return true;
}


An example of adding a callback:
AddCallback(static_cast<PortCallbackFnPtr>(&cGraph::OnNewGraphData),
"cRealMatrix", this);
 
M

mlimber

Hi all

I have a class (cPort) that is designed to receive objects and,
depending on the type, call a handler (callback) in any descendant of a
cProcessBlock class. Callback functions take a shared_ptr<cBase> as a
parameter, and return void.

The code was working fine, although I have encountered problems (under
a Microsoft compiler, of course - VC 8.0) when I attempt to add
callbacks to a class with multiple inheritance. I hate multiple
inheritance in general, but this is necessary to interface with another
library. I think there is some issue regarding the size of member
function pointers in VC.

Anyway - I believe that this code can be converted to use
boost::function and/or boost::bind to iron out these issues. The
problem is that I am going more than slightly squint when I read the
boost docs.

I have attached a reduced set of my code below. I would be extremely
grateful if someone could point out how this code could be converted to
use the boost representations.

Thanks for any and all replies!

Ryan

typedef void (cProcessBlock::*PortCallbackFnPtr)(shared_ptr<cBase>);

// structure that associates a callback function pointer with the
required caller
class cPortCallback : public cBase
{
public:
cPortCallback() :
m_pPortCallbackFn(NULL), m_pCaller(m_pNULL) { }
cPortCallback(PortCallbackFnPtr pPortCallbackFn,
shared_ptr<cBase> pCaller) :
m_pPortCallbackFn(pPortCallbackFn), m_pCaller(pCaller) { }
PortCallbackFnPtr m_pPortCallbackFn;
shared_ptr<cBase> m_pCaller;
};

class cPort : public cProcessBlock
{
public:
/// Add a new callback
void AddCallback(PortCallbackFnPtr NewCallbackFnPtr, std::string
strClassName,
shared_ptr<cBase> pCaller);

protected:
/// Callback registry (map of callback handlers)
map<string, cPortCallback> m_CallbackRegistry;
};

void cPort::AddCallback(PortCallbackFnPtr NewCallbackFnPtr, std::string
strClassName,
shared_ptr<cBase> pCaller)
{
m_CallbackRegistry[strClassName] = cPortCallback(NewCallbackFnPtr,
pCaller);
}

bool cPort::Timeslice()
// (called repeatedly in thread) - continuously receive new objects and
process using callbacks
{
shared_ptr<cBase> pNewObject;
pNewObject = Receive();

// Call appropriate callback depending on the received object's
class name
if (pNewObject) {
PortCallbackFnPtr CallbackFn =
m_CallbackRegistry[pNewObject->ClassName()].m_pPortCallbackFn;
if (CallbackFn) // if appropriate callback handler is found

((*(shared_polymorphic_cast<cProcessBlock>(m_CallbackRegistry[pNewObject->ClassName()].m_pCaller))).*(CallbackFn))(pNewObject);
}
return true;
}


An example of adding a callback:
AddCallback(static_cast<PortCallbackFnPtr>(&cGraph::OnNewGraphData),
"cRealMatrix", this);

You might want to ask on the Boost user's list, but since the
components you're talking about are mostly in TR1 (not counting
shared_polymorphic_cast), if you post a more complete example that
demonstrates the problem and that we can cut and paste into our editors
unchanged (see the guidelines for posting code:
http://parashift.com/c++-faq-lite/how-to-post.html#faq-5.8), we can
probably help you here.

Cheers! --M
 
K

k04jg02

Using boost::bind and boost::function for member function call backs is
actually surprisingly easy. You just make sure the first parameter you
bind is the 'this' object you want to call the member function on.

boost::function<void (void)> f;
f = boost::bind(&MyClass::MyMethod, this);

If you have additional parameters left that should be called on f:

boost::function<void (int, int)> f;
f = boost::bind(&MyClass::MyMethod, this, _1, _2);

This says, "MyMethod is a member function that takes two ints and
returns void. For the first parameter always give it 'this.'" which
effectively makes f a function that now takes 2 variables instead of 3
(before it took the 'this' pointer and two additional parameters). If
you did:

f = boost::bind(&MyClass::MyMethod, this, _1, _1);

Now f will take only 1 parameter, and it will pass it as both the first
and second parameter to MyMethod.

I usually just make the particular boost::function<> template needed a
typedef.
Hi all

I have a class (cPort) that is designed to receive objects and,
depending on the type, call a handler (callback) in any descendant of a
cProcessBlock class. Callback functions take a shared_ptr<cBase> as a
parameter, and return void.

The code was working fine, although I have encountered problems (under
a Microsoft compiler, of course - VC 8.0) when I attempt to add
callbacks to a class with multiple inheritance. I hate multiple
inheritance in general, but this is necessary to interface with another
library. I think there is some issue regarding the size of member
function pointers in VC.

Anyway - I believe that this code can be converted to use
boost::function and/or boost::bind to iron out these issues. The
problem is that I am going more than slightly squint when I read the
boost docs.

I have attached a reduced set of my code below. I would be extremely
grateful if someone could point out how this code could be converted to
use the boost representations.

Thanks for any and all replies!

Ryan

typedef void (cProcessBlock::*PortCallbackFnPtr)(shared_ptr<cBase>);

// structure that associates a callback function pointer with the
required caller
class cPortCallback : public cBase
{
public:
cPortCallback() :
m_pPortCallbackFn(NULL), m_pCaller(m_pNULL) { }
cPortCallback(PortCallbackFnPtr pPortCallbackFn,
shared_ptr<cBase> pCaller) :
m_pPortCallbackFn(pPortCallbackFn), m_pCaller(pCaller) { }
PortCallbackFnPtr m_pPortCallbackFn;
shared_ptr<cBase> m_pCaller;
};

class cPort : public cProcessBlock
{
public:
/// Add a new callback
void AddCallback(PortCallbackFnPtr NewCallbackFnPtr, std::string
strClassName,
shared_ptr<cBase> pCaller);

protected:
/// Callback registry (map of callback handlers)
map<string, cPortCallback> m_CallbackRegistry;
};

void cPort::AddCallback(PortCallbackFnPtr NewCallbackFnPtr, std::string
strClassName,
shared_ptr<cBase> pCaller)
{
m_CallbackRegistry[strClassName] = cPortCallback(NewCallbackFnPtr,
pCaller);
}

bool cPort::Timeslice()
// (called repeatedly in thread) - continuously receive new objects and
process using callbacks
{
shared_ptr<cBase> pNewObject;
pNewObject = Receive();

// Call appropriate callback depending on the received object's
class name
if (pNewObject) {
PortCallbackFnPtr CallbackFn =
m_CallbackRegistry[pNewObject->ClassName()].m_pPortCallbackFn;
if (CallbackFn) // if appropriate callback handler is found

((*(shared_polymorphic_cast<cProcessBlock>(m_CallbackRegistry[pNewObject->ClassName()].m_pCaller))).*(CallbackFn))(pNewObject);
}
return true;
}


An example of adding a callback:
AddCallback(static_cast<PortCallbackFnPtr>(&cGraph::OnNewGraphData),
"cRealMatrix", this);

You might want to ask on the Boost user's list, but since the
components you're talking about are mostly in TR1 (not counting
shared_polymorphic_cast), if you post a more complete example that
demonstrates the problem and that we can cut and paste into our editors
unchanged (see the guidelines for posting code:
http://parashift.com/c++-faq-lite/how-to-post.html#faq-5.8), we can
probably help you here.

Cheers! --M
 
R

ryan.mitchley

Using boost::bind and boost::function for member function call backs is
actually surprisingly easy. You just make sure the first parameter you
bind is the 'this' object you want to call the member function on.

Thanks! The conversion did indeed turn out to be surprisingly more easy
than I was expecting . . .

I hope the performance penalty of the "bind" stuff is not too high, but
I think the clarity of the code is probably worth it.

Ryan
 

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

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top