Thread: Is it possible to have callback function as a class member?

  1. #1
    Registered User
    Join Date
    Dec 2002
    Posts
    162

    Question Is it possible to have callback function as a class member?

    Hi, all

    I am writing a class that creates and maintains a window/control. The problem is that I can’t seem to give the class its own private callback function (the Window Procedure alias WndProc); the VC++ complier simply gives a type cast error when giving out the function pointer. I thought about why and I assume that the reason lies in the different structure between class functions and global functions. But is there a way to go round this, like force the compiler to construct the class function as a global function (assuming that’s the case)? If that’s not that the case, then why does the compiler give a type cast error? And is it possible to have callback function as a class member or at least give the class some sort of private callback function? And oh yes, I know I can use MFC but I simply prefer not, I want to know if can be done without its help.

    Thanks in advance
    Last edited by Aidman; 07-31-2003 at 03:11 AM.
    We haven't inherited Earth from our parents; instead we have borrowed her from our children - old Indian saying.

  2. #2
    &TH of undefined behavior Fordy's Avatar
    Join Date
    Aug 2001
    Posts
    5,793
    No problem....make the callback a static member function...that way it always exists even if there isnt an instance of the class (this is your problem by the way...a member function need the this poiter for it's non static member funcs)

    I use MFC, but I have had to create a window wrapper class as well (For my OpenGL stuff - didnt want MFC to be used there)...nothing wrong with that

    Have a peek at Ken Fitlike's page....he has some stuff on window wrappers

  3. #3
    Registered User
    Join Date
    Dec 2002
    Posts
    162
    Thanks that worked (making the function static), but another problem came up. I get a link error saying “illegal reference to data member 'Class::X' in a static member function”, whenever the static function uses other class members (both variables and other functions). How can I solve this?
    We haven't inherited Earth from our parents; instead we have borrowed her from our children - old Indian saying.

  4. #4
    &TH of undefined behavior Fordy's Avatar
    Join Date
    Aug 2001
    Posts
    5,793
    Yeah...that's the problem you have to get over...all instances of your windows class will use that 1 function....so if you reference any member variables or member functions in that static func, they must be static also......

    There are ways around this.......cant remember exactly how I did it (not near my code right now), but 1 idea might be to use a static std::map member with a pair of <HWND,MyWindowWrapper*>.....you could pass the "this" pointer along with the user param of CreateWindow......then in the WM_CREATE handler, take the hWnd param and cast the lParam to a pointer to a CREATESTRUCT. The lpCreateParams member of CREATESTRUCT can be cast to a MyWindowWrapper pointer (this is the "this" param you passed). Then with the HWND and the MyWindowWrapper*, add them to the map....then in other handlers (WM_SIZE...etc) you call the map with the hWnd param of your WindowProc and you should get a pointer to a MyWindowWrapper which will allow you to access members of that class

  5. #5
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    I ran into this exact problem a while ago, and found a decent solution. A good person to ask is Ken Fitlike, he's good with classes.

    Basically, you need to create two member functions: a static callback procedure, and a normal one, with the same parameters.
    In the static one, the first time it is called, you have to obtain a pointer to the instance of the class you want. Then you can forward the messages to the non-static procedure, which has full access to the class members. Compilcated, I know.

    Code:
    class benny
    {
    public:
    static LRESULT CALLBACK bennyProcA(HWND,UINT,WPARAM,LPARAM);
    LRESULT CALLBACK bennyProc(HWND,UINT,WPARAM,LPARAM);
    int data;
    };
    
    LRESULT CALLBACK benny::bennyProcA(params)
    {
    if (msg==WM_NCCREATE)
    {
    LPCREATESTRUCT lpc;
    
    lpc=(LPCREATESTRUCT)lParam;
    SetWindowLong(hwnd,GWL_USERDATA,(LONG)lpc->lpCreateParams);
    SetWindowLong(hwnd,0,1);
    return DefWindowProc(hwnd,msg,wParam,lParam);
    }
    
    benny *it;
    if (GetWindowLong(hwnd,0))
    {
    it=(benny *)GetWindowLong(hwnd,GWL_USERDATA);
    return it->bennyProc(params);
    }
    
    return DefWindowProc(params);
    }
    
    LRESULT CALLBACK benny::bennyProc(params)
    {
    //code
    }
    It's intensely complicated actually. Basically, the callback procedure has to be static, so that there is an instance to point to from the beginning. But, this means that the procedure CANNOT know where the instance of the CLASS is. So you have to find the class instance yourself, and store that value somewhere permanent. Then you can forward messages all you want. But I don't know how it would be done when not using the window procedure. It just happens that the WM_NCCREATE is the first one to occur, and can contain a pointer to the class.

    If that's too complex, I'll try again.
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  6. #6
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    All WinAPI callbacks have one "user data" parameter. Use it to pass a pointer to a class instance.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  7. #7
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    But is there always somewhere to store the pointer?
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  8. #8
    &TH of undefined behavior Fordy's Avatar
    Join Date
    Aug 2001
    Posts
    5,793
    >>But is there always somewhere to store the pointer?

    As CorenedBee said - yes...each window can have a 32bit user defined data variable with it...accessed with GetWindowLong.....this variable can be used to store the this pointer of a class.....

    The difference between my idea and your example is that I store my pointers in a map for access using a HWND as a key, and with yours, you call GetWindowLong each time you need the pointer to an instance.... either should work, but if the use of your class decides to store their own data in the user defined variable, then you could have a problem with your version....if this might be a problem then you could use my std::map method (I have a sneaky suspicion that MFC uses a map for this stuff....cant tell right now though)

  9. #9
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    Hmm. Would there be callback functions not associated with a window? I mean, sometime or rather, you're going to have to give the address of a member function from a class, and it'll have to be static, and therefore you won't have access to the data members. eg:

    Code:
    class benny
    {
    public:
    static void doStuff();
    (void *__stdcall) GetFuncAddr();
    int data;
    };
    
    (void *__stdcall) benny::GetFuncAddr()
    {
    return doStuff;
    }
    Or something like that. I have no idea about the function return type thing, but that's not the issue. In that case, where a function pointer is returned, and there are no windows associated, where can data be stored?
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  10. #10
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Originally posted by Fordy
    >>But is there always somewhere to store the pointer?

    As CorenedBee said - yes...each window can have a 32bit user defined data variable with it...accessed with GetWindowLong.....this variable can be used to store the this pointer of a class.....
    It can store a lot more than that. You have the 32-bit (64 on 64-bit Win) value by default, but can always have more by specifying a value other than 0 for cbWndExtra in the WNDCLASS.


    The difference between my idea and your example is that I store my pointers in a map for access using a HWND as a key, and with yours, you call GetWindowLong each time you need the pointer to an instance.... either should work, but if the use of your class decides to store their own data in the user defined variable, then you could have a problem with your version....if
    The call to the function requiring a callback is something internal to my class - the user simply doesn't get a chance to pass his own data
    The class can offer an alternative which can then be handled using member variables - but again, this doesn't need to concern the user.

    this might be a problem then you could use my std::map method (I have a sneaky suspicion that MFC uses a map for this stuff....cant tell right now though)
    MFC uses a map (though it's a hash map, not a tree like std::map). This is in part because only this way makes CWnd::FromHandle possible.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  11. #11
    Registered User
    Join Date
    Dec 2002
    Posts
    162
    Thanks for the many replies guys

    I am still thinking about which method to use...
    We haven't inherited Earth from our parents; instead we have borrowed her from our children - old Indian saying.

  12. #12
    erstwhile
    Join Date
    Jan 2002
    Posts
    2,227
    Then use whatever you feel most comfortable with.

    Use of SetWindowLong/GetWindowLong and static window procedures is a fairly typical ie commonplace approach. Using maps as Fordy has described is typical of Fordy.

    Either way ( or whatever unique approach you choose to implement) be assured that you can always get some helpful discussion here.

    Attached is a quick and dirty example (msvc6) for creating a dialog which may be of some interest.

    Good luck.
    CProgramming FAQ
    Caution: this person may be a carrier of the misinformation virus.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 4
    Last Post: 05-13-2011, 08:28 AM
  2. Message class ** Need help befor 12am tonight**
    By TransformedBG in forum C++ Programming
    Replies: 1
    Last Post: 11-29-2006, 11:03 PM
  3. Replies: 28
    Last Post: 07-16-2006, 11:35 PM
  4. My Window Class
    By Epo in forum Game Programming
    Replies: 2
    Last Post: 07-10-2005, 02:33 PM
  5. Staticly Bound Member Function Pointers
    By Polymorphic OOP in forum C++ Programming
    Replies: 29
    Last Post: 11-28-2002, 01:18 PM