Advertisement

prototyping Callback with methods in C++

Started by April 11, 2001 01:05 PM
3 comments, last by codemonkey 23 years, 9 months ago
Does anyone know a good way to prototype a function callback (that is inside a class) without having to make the class method static and pass ''this'' as a pointer to the function? Example: class CMyClass { public: static NeatMethod(PVOID pThis); }; CMyClass::NeatMethod(PVOID pThis) { CMyClass *pMyClass = (CMyClass *)pThis; // Do stuff, using pMyClass-> } The problem is that I want to prototype (if possible) the implicit ''this'' pointer that the C++ ruleset automatically. Since C++ implicitally passes ''this'' I have to do it manually like above. What I would like to do is typedefine a cool callback prototype like: typedef void CBFUNC(); And create a variable like: // Assuming NetMethod IS NOT static CMyClass mine; CBFUNC *Fn = mine.NeatMethod; Without MS VC++ giving me prototype issues because "NetMethod" takes one parameter (under the hood) that is equivilent to the ''this'' pointer. My static method soution is: typedef void CBFUNC(PVOID); CMyClass mine; CBFUNC *Fn = mine.NeatMethod; And that works, because NeatMethod IS indeed static. Although my implementation works, I would love to know if it is indeed possible to get around this static issue. Any ideas? Thanks, CodeMonkey
CODE Monkey

#include &ltiostream>
#include &ltfunctional>

using std::cout;
using std::endl;
using std::mem_fun;

class myclass
{
public:
int member_function() { cout << "whoohoo! no static or explicit casts!" << endl; return 0; }
};

// Some function somewhere in your app that wants a callback
template
void function(myclass* instance, Function f)
{
f(instance); // turns into instance->member_function() if myclass::member_function was passed as pointer
}

int main()
{
myclass instance;

// call myclass::member_function using global function
function(&instance, mem_fun(myclass::member_function));
}



You could also use mem_fun on a virtual function, and call it through the base pointer. std::mem_fun just handles the pointer-to-member syntax and conversions for you.

Note that MSVC can''t properly handle the expression "return void;", so you can''t pass a member function that has a return value of "void" to mem_fun. I just used int as the return type and returned 0, that''s really what the compiler does for void in assembly anyway.
Advertisement
Ok, that part makes sense... now lets add some more tricky stuff. I don''t know the class type for the "instance".

For example, I just call:

fn(1,"hi", 4.1);

Where fn is the data-type CBFUNC * which looks like:

typedef CBFUNC void func(int,char *, float);

So VC++ says that''s ok IF your function callback address takes three parameters. But when it comes to a class it really takes 4 parameters (that damn ''this'' pointer). However at this point it could be one of any class that I have created.

My solution for now is to pass the instance as a void pointer and cast it over to the callback ONCE that callback is called. (as seen in my first example). However I don''t know what function it is until the fn(1,"hi", 4.1) actually executes.

Your solution seems to be to prototype the instance as the first parameter by making it cmyclass *instance... but that only works if I know it is a cmyclass object. So I was hoping there was a way to say that my function implicitally takes a ''this'' pointer without having to prototype it in as a PVOID data-type.

like

typedef CBFUNC void func(__implicit_this, int, char*, float)

Of coures I doubt there is an __implicit_this keyword..but ya know what I''m getting at? So that way CMyClass.NeatMethod(int, char*, float), CYourClass.NewMethod(int,char*,float), CNewClass.Function(int, char*, float) would all be used with the same prototyped CBFUNC.

Does that makes sense? or is it doable?

CodeMonkey
PS: I like that code example though, it does give you a little more power to do new things. [copy! paste!]
CODE Monkey
You could use operator():

      class Callback{public:    virtual void operator()(int i, float f, bool b) = 0;};template<type T>class MemberCallback : public Callback{    T* instance;    void (T::*ptm)(int i, float f, bool b);public:    MemberCallback(T* ptr, void (T::*func)(int i, float f, bool b)    {        instance = ptr;        ptm = func;    }    void operator()(int i, float f, bool b)    {        instance->*ptm(i,f,b);    }};class Test{    TestFunc(int i, float f, bool b) { ... }};MemberCallback<Test> callback(new Test, Test::TestFunc);// much latervoid someRandomPlace(){    int i = getintdata();    float f = getfloatdata();    bool b = someObject.isValid();    callback(i,f,b);}  


Of course you do not have to use templates:

      class TestCallback : public Callback{    Test* instance;public:    TestCallback(Test* pTest) { instance = pTest; }    void operator()(int i, float f, bool b)    {        instance->TestFunc(i,f,b);    }};  


The point is, if you do not want to have to pass the this pointer at the time of the call, you must save the this pointer earlier (at the point of the callback creation, in this case).

The best method in my opinion is to use an abstract callback class. For example, if you wanted to call a 'normal' function instead, derive a class like this:

        class FunctionCallback : public Callback{    void (*pf)(int i, float f, bool b);public:    FunctionCallback(void (*func)(int i, float f, bool b))    { pf = func; }    void operator()(int i, float f, bool b)    { (*pf)(i,f,b); }};void DoSomething(int i, float f, bool b);FunctionCallback callback(DoSomething);  



Edited by - Nocturnal on April 12, 2001 5:04:54 PM
quote:
Original post by codemonkey

Ok, that part makes sense... now lets add some more tricky stuff. I don''t know the class type for the "instance".



If the type cannot be known until run-time, you might want to consider inheritance and virtual functions. Define an abstract base class (empty) that just serves as a list of functions that every class passed to your function must implement.


#include &ltiostream>

using std::cout;
using std::endl;

class interface
{
public:
// The " = 0" means that derived classes must implement these functions!
virtual void greet() = 0;
virtual void leave() = 0;
};

class formal : public interface
{
public:
virtual void greet() { cout << "hello" << endl; }
virtual void leave() { cout << "goodbye!" << end; }
};

class informal : public interface
{
public:
virtual void greet() { cout << "hey" << endl; }
virtual void leave() { cout << "see ya" << endl; }
};

// Some function that wants to use any class conforming to the interface
void some_function(interface* p)
{
// output actual class name, not known until run-time
cout << "<" << typeid(*p).name() << ">" << endl;

// output some greetings and goodbyes
p->greet();
p->leave();

// output another line, to separate the next call of some_function
cout << endl;
}

// Illustrate the solution by making two difference classes and
// passing them to the same function. The type doesn''t have to
// be known as it is resolved at run-time.
int main()
{
formal f;
informal i;

function(&f);
function(&i);
}



The output should be:


&ltclass formal>
hello
goodbye!

&ltclass informal>
hey
see ya



The only real difference is that the function pointers are passed to the function implicitly with the instance, as opposed to passing the instance implicitly with the function pointers (as with Nocturnal''s example).

This topic is closed to new replies.

Advertisement