Thread: illegal references in static member functions

  1. #1
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401

    illegal references in static member functions

    my class has a static member window procedure. from this procedure i cant refer to any data members of that class without the compiler telling me:

    illegal reference to data member 'cBen::value' in a static member function

    ie:

    Code:
    class cBen
    {
    public:
        static LRESULT CALLBACK WndProc(...);
        int value;
    };
    
    LRESULT CALLBACK cBen::WndProc(...)
    {
        value=5;
        return DefWindowProc(...);
    }
    what can i do?
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  2. #2
    Code Monkey Davros's Avatar
    Join Date
    Jun 2002
    Posts
    812
    You could make value static also, like:

    static int value;

  3. #3
    erstwhile
    Join Date
    Jan 2002
    Posts
    2,227
    An alternative to Davros's fine suggestion and arguably a more typical approach to what you are attempting is to retrieve a pointer to your class object and work through that within your static wndproc.

    You can make your class object a global variable and use it directly - good for 'proof of function' or, more usually, you store a pointer to your class object when you create your window using CreateWindowEx, setting the last parameter of that function to this. You retrieve this in your static wndproc with GetWindowLongPtr (GetWindowLong works too) and store it with SetWindowLongPtr (SetWindowLong works too), initially obtaining it from the (CREATESTRUCT*)lParam of the WM_NCCREATE message, and using SetWindowLongPtr to store it. Subsequent messages can use GetWindowLongPtr to obtain a valid pointer. There are other ways to achieve the same and much of this is discussed in those links I suggested to you in your initial thread asking for a non-global wndproc solution, which might be worth a re-read.

    I think there is an ancient msdn article that discusses this approach and I will post a link to it if I find it.

    Good luck anyway.

  4. #4
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    davros, i cant get your method to work, and anyway, it seems a bit annoying to have to declare everything static if i want it to be usable. heres what happens:

    Code:
    class cBen
    {
    public:
        static int value;
        static LRESULT CALLBACK proc(...);
    };
    
    LRESULT CALLBACK cBen::proc(...)
    {
        value=5;
    }
    
    compiler error:
    cBen.obj : error LNK2001: unresolved external symbol "public: static int cBen::value" (?value@cBen@@2HA)
    i have no idea what THAT means.

    ken, i cant work how to use SetWindowLongPtr to set the class pointer. there doesn't seem to be a pre-defined offset for that information. how do i do it?
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  5. #5
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    and also davros, reading on MSDN i found that declaring a data member of a class static means that one copy of the data member is shared by all instances of the class, and that does not sound like what i want.

    i think the problem can be resolved way back when i asked about window procedures within classes. according to MSDN, declaring a member function to be static means that it can only access static members of that class. that was not the goal that i was trying to achieve. all i wanted was to be able to set the wndproc of a WNDCLASSEX structure to a member function window procedure, and for some reason, static made that work. but is there any other way to do it?
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  6. #6
    Registered User johnnie2's Avatar
    Join Date
    Aug 2001
    Posts
    186
    You'll have to do this to get rid of the undefined external symbol (that static variable value):

    Code:
    class cBen {
       public:
          static int value;
          static LRESULT CALLBACK proc(...);
    };
    
    static int cBen::value;
    
    
    // then the use of value becomes valid
    That is, redeclare the static variable outside the class definition. This is a consequence of the way compilers used to register static class variables. Some saw static variables declared within the class definition valid, while others only recognized static variables defined outside the declaration. As a result, the standard requires the programmer to declare the variable both ways.

    Search Google or the boards for a more in-depth explanation.
    "Optimal decisions, once made, do not need to be changed." - Robert Sedgewick, Algorithms in C

  7. #7
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    nup, that doesn't work. the redeclaration causes the following compiler error:

    'value' : 'static ' storage-class specifier illegal on members

    anyway, i really dont want all my data members to be static. i would rather find a way to use the window procedure without it being static. thanks anyway.
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  8. #8
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    okay, i think i just figured something out. the reason the wndproc member function must be static is because in the Create function i try to pass a pointer to the function while initializing the WNDCLASSEX structure. So that means the pointer MUST point to a valid function, and therefore an instance of the class or at least that function must be in existence. static makes sure there is always an instance available.

    but that's pretty crappy. why can't i pass a pointer that refers to the wndproc that is created with the instance that is created? i hope you catch my drift. i mean, say you have the following:

    Code:
    class cBen
    {
    LRESULT CALLBACK cBenProc();   
    blah blah
    };
    
    cBen::Create()
    {
        WNDCLASSEX wcx;
    
         wcx.lpfnWndProc=(WNDPROC)cBenProc;
    }
    
    cBen::cBenProc()
    {
      blah blah
    }
    
    cBen instBen;
    
    int WINAPI WinMain(...)
    {
        instBen.Create();
    }
    when instBen is created, shouldn't an instance of cBenProc() be created to? therefore, isn't the pointer being passed a valid pointer? i am confused.
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  9. #9
    erstwhile
    Join Date
    Jan 2002
    Posts
    2,227
    1. Store a pointer to your class object in your window by setting the last parameter of CreateWindowEx to this.

    2. Set the lpfnWndProc parameter of the WNDCLASSEX struct to the name of your static wndproc.

    3. In your static wndproc check for WM_NCCREATE, cast the lParam to a CREATESTRUCT pointer of which the lpCreateParams is your class object pointer. Cast that value to get a usable ptr to your class.

    4. Still within your WM_NCCREATE handler, store the class ptr obtained in 3 using SetWindowLongPtr or SetWindowLong with the GWL_USERDATA flag. Use the pointer to call your class message handler function, if you have one or use DefWindowProc as normal.

    5. For all subsequent messages use GetWindowLongPtr (or GetWindowLong) to retrieve your class pointer (same GWL_USERDATA flag, casting to the required type for your class object) and use that to call your class handler for that particular message.

    6. For unhandled messages/default system handling call DefWindowProc as normal.

    Notes:

    1. WM_NCCREATE, WM_CREATE and a couple of other msgs are sent during processing of CreateWindowEx, so you lose the capability to properly handle messages sent to your window prior to WM_NCCREATE, the first point at which your class pointer becomes available to your static wndproc.

    2. Static procedures cannot be virtual so it's a good idea to have a virtual wndproc which the static wndproc forwards messages to for cracking/handling.

    >>why can't i pass a pointer that refers to the wndproc that is created with the instance that is created<<

    Because your class wndproc has an implicit this ie is NOT of type WNDPROC (see winuser.h for its declaration) required by WNDCLASSEX, so use a static window procedure instead.

  10. #10
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    thanks ken, that works, but when i tried to make it a bit more efficient, i ran into some trouble.
    observe: (by the way, i allocated an extra byte to the window memory)

    Code:
    LRESULT CALLBACK cBen::cBenProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
    {
    	cBen *it;
    
    	if (GetWindowLong(hwnd,0))
    		it=(cBen *)GetWindowLong(hwnd,GWL_USERDATA);
    	
    
    	switch (msg)
    	{
    	case WM_NCCREATE:
    		LPCREATESTRUCT lpc;
    
    		lpc=(LPCREATESTRUCT)lParam;
    		SetWindowLong(hwnd,GWL_USERDATA,(LONG)lpc->lpCreateParams);
    		SetWindowLong(hwnd,0,1);
    		return DefWindowProc(hwnd,msg,wParam,lParam);
    
    	case WM_CLOSE:
    		it->value=5;
    
    		DestroyWindow(hwnd);
    		return 0;
    
    	case WM_DESTROY:
    		return 0;
    
    	default:
    		return DefWindowProc(hwnd,msg,wParam,lParam);
    
    	}
    }
    clearly, i'm trying to make it so i only ever have to retrieve a pointer once during the function. but the line:
    Code:
    it->value=5;
    causes one of those annoying access violations.
    Code:
    First-chance exception in class.exe: 0xC0000005: Access Violation.
    If im not mistaken, the pointer i retrieved points to somewhere i shouldn't be changing. however, it works fine when the pointer is retrieved within the WM_CLOSE message capture, and the code has not been modified, just moved. so what is going on?

    damn i hate this static crap! if only the wndproc could avoid being static, then it would be easy. are you sure it has to be so difficult?
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  11. #11
    Code Monkey Davros's Avatar
    Join Date
    Jun 2002
    Posts
    812
    >An alternative to Davros's fine suggestion and arguably a more typical approach to what you are attempting is to retrieve a pointer to your class object and work through that within your static wndproc.

    I would agree with that. If you can pass a pointer of the object through the callback function, this is better. Using static data will work, but will never be thread safe because the same data is shared for all object instances.

    If ever you need to use static data in the future, you need to initialise them also, like so:

    (header file)
    Code:
    ..
    public:
        static int value;
    ..
    (and cpp file)
    Code:
    int cBen::value = 0;

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Problem with Free.
    By chakra in forum C Programming
    Replies: 9
    Last Post: 12-15-2008, 11:20 AM
  2. Static & Data Member
    By ecoliteracy in forum C++ Programming
    Replies: 1
    Last Post: 04-16-2007, 08:46 PM
  3. Static functions.... why?
    By patricio2626 in forum C++ Programming
    Replies: 4
    Last Post: 04-02-2007, 08:06 PM
  4. Static Functions
    By Dae in forum C++ Programming
    Replies: 4
    Last Post: 10-23-2005, 08:45 PM
  5. SSH Hacker Activity!! AAHHH!!
    By Kleid-0 in forum A Brief History of Cprogramming.com
    Replies: 15
    Last Post: 03-06-2005, 03:53 PM