Class-to-class callback?

Started by
12 comments, last by s73obrien 12 years, 4 months ago
Okay, this is likely a repost, but it's an important topic, and one that is a huge stumbling block for me.

I have a class (It's name is Class_A in this post :) ) that needs to callback to a member function of another class (Class_B), like so:


void Class_A::member_function()
{
... //do stuff
this->pointer_to_Class_B_function(this);
}


The function in Class_B needs to have access to both Class_A and Class_B (which means I can't just use static for the Class_B func)

My question is: What is the best way of going about this? I'd like to have it so I don't have to modify Class_B in any specific way to 'fit' Class_A, if possible. Class_A is a COM object, if that helps...
Advertisement

Okay, this is likely a repost, but it's an important topic, and one that is a huge stumbling block for me.

I have a class (It's name is Class_A in this post :) ) that needs to callback to a member function of another class (Class_B), like so:


void Class_A::member_function()
{
... //do stuff
this->pointer_to_Class_B_function(this);
}


The function in Class_B needs to have access to both Class_A and Class_B (which means I can't just use static for the Class_B func)

My question is: What is the best way of going about this? I'd like to have it so I don't have to modify Class_B in any specific way to 'fit' Class_A, if possible. Class_A is a COM object, if that helps...


Declare a function as friend function to both of these two classes.
I'm not sure I understand the problem. If A and B are tightly coupled in that B needs to talk to A's interface, then so be it; the code you have is the correct approach.

Better said: you don't actually have a problem here, as stated. If there's more to this, it'd be helpful to know the context and why exactly you feel there is an issue; if it's just a matter of "A and B are interdependent" then think for a minute about whether or not you really care that they are interdependent. (Tight coupling is, occasionally, a perfectly good solution to a problem.) If you do care, fix your design so A and B are not coupled. If you don't, then take off early and have a beer ;-)

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

I apologize for the lack of context, ApochPiQ. I am reluctant to divulge too much, as I am dealing in the realm of the proprietary w/ this project. Here's some more :)

Class_B is an application class. It holds information on the creation of a window (this is a Win32 project atm btw) and processing of its messages, along with other application-specific information.
I have defined a user-interface callback inside that class that is called by Class_A at the completion of a particular task to update the window held by Class_B. Class_A, as stated before, is a COM object. It is instantiated and used from within Class_B.

The problem is, because I am using a callback mechanism, I am currently forced to use a static member of Class_B as the callback. This, of course, makes it so Class_A cannot pass in the pointer to Class_B when making the call (as, I admit, it should probably be). The callback function has the permission to access Class_B (since it *is* a member function of it), it just doesn't know where to look. The callback function also has to be able to access Class_A's interface to retrieve the information, which can (and is) achieved through a simple pointer argument. Since Class_A is a component that I'd like to reuse, I want to keep coupling to an absolute minimum.

I have found a solution in passing the handle to the window to the callback and then using that handle to retrieve Class_B's pointer from an OS-dependent variable cache associated with said window, but I really really want to stay away from any dependency on any OS in Class_A's code, as this will likely be ported at some point in the future. Also, it feels very clunky and inefficient and I was hoping that mayhaps there was a language feature that I wasn't aware of to achieve the desired results.

Using a friend function to both classes is a bit of an option, but that means I would have to declare a specific function name inside Class_A, effectively locking the app programmer into using that particular function name, which I simply will not do.

I appreciate any and all help on this.
Does your callback mechanism let you provide custom contextual data, eg. via a void* or a pUnknown or something similar?

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Yeah, it does. That's how I pass in the pointer to Class_A. Unfortunately, at the time of execution, Class_A knows nothing about Class_B, so cannot pass in a pointer to it.

Basically, I've come to the conclusion that the function in Class_B has to either: use a this-style reference to itself (which is precluded by the enforced usage of a static member function) or produce a pointer to itself out of thin air. I have chosen the latter. It works, but I would still rather have the former.
This really doesn't make much sense to me... how do you know what Class_B instance to operate on at the time of the callback invocation, but not when the callback is set up?

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]


Yeah, it does. That's how I pass in the pointer to Class_A. Unfortunately, at the time of execution, Class_A knows nothing about Class_B, so cannot pass in a pointer to it.

Basically, I've come to the conclusion that the function in Class_B has to either: use a this-style reference to itself (which is precluded by the enforced usage of a static member function) or produce a pointer to itself out of thin air. I have chosen the latter. It works, but I would still rather have the former.


To edit this for clarity: Class_A doesn't know about Class_B at the time of executing the callback. At one point, when telling A about the callback function, I do have the opportunity to pass in a pointer to B, but there is no way to make it type-safe, since A isn't supposed to know the details of B, so instead, I pass in a pointer-to-static-member for B using a predefined function signature. A small example program to illustrate:


#include <iostream>

class Class_A
{
void run();
void set_callback(void (*)(void*));

private:
void (*callback)(void*);
};

void Class_A::run()
{
this->callback(this);
}

void Class_A::set_callback(void(*func_to_callback)(void*))
{
this->callback = func_to_callback;
}

class Class_B
{
static void func_to_be_called(void*);
};

void Class_B::func_to_be_called(void* pointer_to_A)
{
//do stuff with Class_A and Class_B
std::cout << "Callback function called" << std::endl;
std::cout << "Class_A = " << (long)pointer_to_A << std::endl;


//notice that Class_B cannot be accessed because this is a static function
//and pointer to B is not passed in
}

int main()
{
Class_A A;
Class_B B;

A.set_callback(B.func_to_be_called)

A.run();

return 0;
}



Looking at this simplified example, I think I see what you are saying in that I could pass in a pointer to B when setting the callback. I suppose that'll probably work, as long as Class_A doesn't have to *do* anything with said pointer, just pass it into the callback. I now see the error of my ways, thank you :)
Callbacks often use void* for precisely this kind of thing :-) You can simply store both the callback target function and the target B instance (in a void*) and you're done.

A much cleaner approach is to have B derive from an interface that A understands. Then A can call the function directly without a function pointer involved at all. This makes the link explicit and allows you to easily understand what sorts of classes might take the role of "B" - i.e. only those that implement the callback interface needed for talking to A.

Since you mentioned COM, a perfect option here is to pass the B pointer into A using a pUnknown, then QueryInterface to obtain a compatible callback interface, and invoke that.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Your second suggestion worked beautifully, once I figured out I had to specify that the callback class is virtual in Class_B's declaration. I think I'll try elevating this to a COM-based solution in the near future, but first, I have to go back and fix up the way I've been using callbacks throughout (been using exclusively C-style callbacks). Yet another casualty of my programmatic upbringing as a procedural coder :)

Thank you very much for your help ApochPiQ. I knew there was a better way to do it, just wasn't sure how.

This topic is closed to new replies.

Advertisement