Advertisement

CALLBACK's vs My class on the heap.

Started by May 18, 2001 06:04 AM
5 comments, last by gimp 23 years, 8 months ago
I have a little problem where I want to set the following callback : LRESULT CALLBACK CRenderer::WindowsMessages(int code, WPARAM wparam, LPARAM lparam) to be able to be called in a normal non-static manner. The calling convention specifies __stdcall. What i want to do essentially is : new CRenderer(); and have the callback fire and return information back to the class. IN this case I want to detect WM_SIZING so I can get the renderer to resize it''s viewport. I can seem to get it to work without declaring it static. If it is static then I can''t call the ''resize'' function on the class(and i don''t want to make everything static. I might want to have multiple renderers at some point.(for different windows, split screen etc). How would you handle this? Many thanks Chris
Chris Brodie
There are many ways to handle this kind of situation. This design relates well to the "Observer" pattern. Basically users of CRenderer register with that class that they''d like to receive the event. Then, when the event fires, CRenderer iterates through all the registered classes and notifies them via a member function such as Notify(MessageType).

The problem that you''ll run into that requires a lot of thought is concurrency. When CRenderer calls Notify() on each registered class it does so in a blocking manner. If one class hangs in Notify() or doesn''t return immediately, CRenderer can''t continue processing. One way to solve this is to create a message delivery queue that works on a separate thread and serializes messages from CRenderer (lets call him the subject).

I''ll see if I can find an example for you. In the mean time, can you provide a bit more information on what CRenderer''s task is and how client classes interact with it.

Dire Wolf
direwolf@digitalfiends.com

Dire Wolf
www.digitalfiends.com
[email=direwolf@digitalfiends.com]Dire Wolf[/email]
www.digitalfiends.com
Advertisement
This is the classic "why can''t I get a C function pointer to a member in a class" question.

The compiler passes a hidden "this" pointer any time you call member functions (for all instances of the class, you only have one instance of the member function and the compiler builds in a hidden this parameter so that the function uses the correct data).


There are two common methods for this:

1. You have a global C-style (stdcall) function for the Windows message callback, this function then looks at the window handle and searches a list of active CRenderer objects checking each of their HWNDs against the HWND the message is intended for, then the function routes the call to that specific object. This is the method that MFC uses. Something like:

  CALLBACK GlobalWndProc(...HWND hWnd, message...){  for (int i=0; i<g_iNumActiveCRendererObjects; ++i)  {    if (hWnd==g_renderer[i].GetHWND())    {      return g_renderer[i].LocalWndProc( hWnd, message );    }  }  return DefWndProc(...)}  



2. An alternative I use is to store the "this" pointer in the window itself, then use a static WndProc, then retrieve the "this" pointer to call the local WndProc:

  class Thing{...  void OpenOrInitialiseWindow();protected:  static BOOL CALLBACK WndProc( HWND hWnd, UINT uMsg, ... )  virtual BOOL OnWndProc( HWND hWnd, UINT uMsg, ... )};void Thing::OpenOrInitialiseWindow(){  WNDCLASS wc;  ...  wc.lpfnWndProc = Thing::WndProc;  ...  RegisterClass();  hWnd = CreateWindow(...);  // stash a ptr to the object in window  SetWindowLong( hWnd, GWL_USERDATA, this );}BOOL CALLBACK Thing::WndProc( HWND hWnd, UINT uMsg, ... ){  Thing* pthis = (Thing*)GetWindowLong(hWnd,GWL_USERDATA);  // need to check this pointer , because we''ll get NCCREATE  // messages before we get a chance to SetWindowLong()  if (pthis)  {    return pthis->OnWndProc( hWnd, uMsg, ... );  }  return DefWndProc(...);}  


--
Simon O''''Connor
Creative Asylum Ltd
www.creative-asylum.com

Simon O'Connor | Technical Director (Newcastle) Lockwood Publishing | LinkedIn | Personal site

Cool!,

I would never have thought of storing the pointer to a class in the window''s storage space...Very inventive. I think I might go with that idea as it seem the lightest and most enclosed. THanks also Dire.Wolf, I''ll keep your idea in mind for future problems.

Thanks all

Chris
Chris Brodie
flipcode recently posted a code of the day on this. Might want to check that out.

Aaron
----------------------
Modian
Thanks... I go there now...

(This is now working just fine btw thanks to Simon again, thats 3 times this week).
Chris Brodie
Advertisement
Ideally the window would allow you to attach observers to it's event pump. Then you're graphics engine would be a IWindowObserver (which makes perfect sense to me).

If you're using MFC it'll provide a class context from which you can get at the graphics engine instance. If you don't like MFC, then check out the new WTL. If you want to write your own CWnd class, then you stuff a context pointer into the GWL_USERDATA area as S1CA showed above.

If you want it all to happen magically (well automatically anyway), make an ActiveX control that uses your graphics engine.



Skeleton of the observer pattern, and basic impl. if you want to check it out.

    //Observer.hpp#include <list>#define FOR_EACH_OBSERVER_CALL(mthd) 	{	tyListObserver::iterator it = m_listObserver.beign();	tyListObserver::iterator itStop = m_listObserver.end();	for(;it!=itStop; it++)		{		(*it)->mthd;		}	}//maybe the pre-processor isn't evil afterall...template <class TObserver>CObservable	{	public:		void AddObserver(TObserver* pObserver)			{			m_listObserver.insert(m_listObserver.end(), pObserver);			}		void RemoveObserver(TObserver*)			{			m_listObserver.remove(pObserver);			}	private:		typedef std::list<TObserver*> tyListObserver;		tyListObserver m_listObserver;	};//Engine classesclass IObserverOfMyClass	{	virtual void OnSomeEvent(int)=0;	};class CMyClass : public IObservable<IObserverOfMyClass>{	private:		void SomeEvent(...)			{			FOR_EACH_OBSERVER_CALL(OnSomeEvent(-1));			}}//UI Class(es)class CWnd : public IOberverOfMyClass	{		CWnd()		{		m_MyInst.AddOberver(this);		}	~CWnd()		{		m_MyInst.RemoveOberver(this);		}		virtual void OnSomeEvent(int i)		{//SomeEvent triggered		_ASSERT(i==-1);		}	private:		CMyClass m_MyInst;	};    


Magmai Kai Holmlor
- The disgruntled & disillusioned


Edited by - Magmai Kai Holmlor on May 22, 2001 12:51:16 AM
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara

This topic is closed to new replies.

Advertisement