   How cross-platform should the code be?
  Hey,I was just fiddling around with throwing together some window creation code and I came to the event handling and it raised some questions.Window creation using Win32 and SDL is pretty similar, OK so Win32 has more code and stuff to do, but essentially from a class point of view you could have something like this:class CGLWindow{public:    CGLWindow();///Class constructor (initialize variables here)    ~CGLWindow();///Class destructor///Creates the OpenGL window to the specified attributesbool CreateGLWindow(int width = 640, int height = 480, int bpp = 16, bool fullscreen = false);///Perform OpenGL state initializationbool InitializeGL();///Handle resize eventsvoid OnResize(int widthIn, int heightIn);///Perform any pre-rendering functions, use deltaTime to keep things constantvoid PrepareRender(float deltaTime);///Render OpenGL void Render();///Shutdown OpenGL and deallocate resourcesvoid Shutdown();private:int m_Width;///Window width and height valuesint m_Height;int m_Bpp;///The colour depth of the windowbool m_Fullscreen;///Should the window be fullscreen??};So in theory the function bodies will differ but the class prototype can remain the same, (I think this is what we should aim for with different versions of the code then people can see how different APIs do things differently). Now for the problems, when handling events in Win32 you need to do some clever (and rather restrictive) stuff with WndProc during the setup of the window, and integrating WndProc into a class means clever tricks with static functions, whereas with SDL you can handle messages pretty much how you want. Also Win32 requires access to a window handle (HWND), rendering context (HGLRC) and device context(HDC) (which should be members in the class) SDL does not.So where am I going with this? I have the following questions for discussion:1.) Should we put #ifdef _WIN32_ type stuff in the class prototypes in all versions of the code for it to remain uniform?2.) Should the event handling be incorporated into the class (not simple but could prove a better choice) or remain outside (simple)?3.) Should all versions of the code differ completely (am I just being idealistic)?4.) How does that class prototype look for a start?Luke.   
  I once tried to get the WndProc into a class and I ran into so many errors like: cannot acces static member...So I just used a function pointer which can be passed to the class and is then used as Event handling proc.But I don't know how we should deal with this here, cause we want to "hide" API-specific stuff from the user whithin his main.cpp or something like this, hmm [help][depressed]But looks quite good as a starting point, and I don't think we should use different code for all API's.   ;  Well, I think we should handle events as described in this topic and under the per-window data title, here.I can't think of any way that we can make the class prototype uniform across all platforms, windows needs the handle and device and rendering contexts and SDL hides these from us. So I reckon that a possible class could look something like this:class CGLWindow{public:    CGLWindow();///Class constructor (initialize variables here)    ~CGLWindow();///Class destructor///Creates the OpenGL window to the specified attributesbool CreateGLWindow(int width = 640, int height = 480, int bpp = 16, bool fullscreen = false);///Perform OpenGL state initializationbool InitializeGL();///Handle resize eventsvoid OnResize(int widthIn, int heightIn);///Perform any pre-rendering functions, use deltaTime to keep things constantvoid PrepareRender(float deltaTime);///Render OpenGL void Render();///Shutdown OpenGL and deallocate resourcesvoid Shutdown();#ifdef WIN32 static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);   LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam);#else   void HandleEvents(); //For SDL #endifprivate:#ifdef WIN32   HWND   m_HWnd;   HGLRC  m_HRC;   HDC    m_HDC;#endifint m_Width;///Window width and height valuesint m_Height;int m_Bpp;///The colour depth of the windowbool m_Fullscreen;///Should the window be fullscreen??   std::string m_WindowCaption;  //The windows title};Now I know that the #ifdef seem nasty but this way we can use the same header file in both versions of the code, this will make it easy to explain we can just say:"Windows requires specific extra data and functions to handle events and creation windows, blah blah"I think this is something we should try to stick to for all classes, the same class prototypes for all versions but different function bodies.Anyone have any other suggestions?Luke.   ;  First off, it doesn't match the coding standards on the wiki, although admittedly we've yet to agree on those coding standards.If we're using modern C++ with RAII then resource should be acquired in the constructor and released in the destructor, which means CreateGLWindow and Shutdown shouldn't exist (they should be the constructor and destructor respectively) and InitializeGL probably shouldn't exist (either it should be part of the constructor or the responsibility of another class).Since we have a resource which needs to be acquired and released we have to consider the rule of three - if a class requires one of destructor, copy-constructor and/or copy-assignment operator it probably needs all three.  In this case we could decide that a window is non-copyable and declare the copy-constructor and copy-assignment operator private and not define them, or we could provide a copy-constructor and copy-assignment operator and perform duplication or reference counting of resources.  For early lessons I'd probably prefer making windows non-copyable, but with the option to change that in a later lesson.Regarding cross-platform issues I would advise against the approach in Kazade's latest post as it means lumping a lot of implementation-specific detail together with little separation.  Sutter and Alexandrescu advise:Quote:C++ Coding Standards, Item 16: Avoid macrosFor conditional compilation (e.g. system-dependant parts), avoid littering your code with #ifdefs.  Instead, prefer to organize code such that the use of macros drives alternative implementations of one common interface, and then use the interface throughout.There are a few ways of achieving this.  We could build an abstract gl_window class and then derive win32_gl_window, sdl_gl_window etc. from that.  We could have separate files, each containing a platform specific implementation of gl_window and use a macro to control which one gets included.  We could implement each platform specific implementation in its own namespace, i.e. win32::gl_window, sdl::gl_window, use a macro to control which one gets included and then typedef it into our main namespace (typedef win32::gl_window gl_window;).  The first is probably better in terms of code-reuse but means explaining inheritance and probably pure virtual functions.  I'm drawn to the third, which is slightly more complex than the second but is slightly kinder on inexperienced programmers (accidentally including two or more files would lead to obscure duplicate definition linker errors).I shall try to add more tomorrow.Σnigma   ;  Hey,I know the code doesn't match the standards in the Wiki, I'm just throwing ideas around to see what people think. I don't expect this to the be actual class at all, I just wanted to get ideas from people about how we are going to structure this.Quote:If we're using modern C++ with RAII then resource should be acquired in the constructor and released in the destructor, which means CreateGLWindow and Shutdown shouldn't exist (they should be the constructor and destructor respectively) and InitializeGL probably shouldn't exist (either it should be part of the constructor or the responsibility of another class).I disagree slightly here, keeping the code broken into smaller functions is a good idea. Sticking a page worth of Win32 initialisation code, and the OpenGL Initialisation stuff in the Constructor is going to be messy and not easily understood by someone trying to learn (let alone harder for us to explain). If its decided that the window should be initialised in the constructor then make CreateGLWindow and InitializeGL private and call them from the constructor. The same for Shutdown, just called in the destructor. It could be just me but I like tidy (con/de)structors [smile]Quote:Since we have a resource which needs to be acquired and released we have to consider the rule of three - if a class requires one of destructor, copy-constructor and/or copy-assignment operator it probably needs all three. In this case we could decide that a window is non-copyable and declare the copy-constructor and copy-assignment operator private and not define them, or we could provide a copy-constructor and copy-assignment operator and perform duplication or reference counting of resources. For early lessons I'd probably prefer making windows non-copyable, but with the option to change that in a later lesson.I totally agree with this.Quote:Regarding cross-platform issues I would advise against the approach in Kazade's latest post as it means lumping a lot of implementation-specific detail together with little separation. Sutter and Alexandrescu advise:Quote:    Quote:    C++ Coding Standards, Item 16: Avoid macros    For conditional compilation (e.g. system-dependant parts), avoid littering your code with #ifdefs. Instead, prefer to organize code such that the use of macros drives alternative implementations of one common interface, and then use the interface throughout.There are a few ways of achieving this. We could build an abstract gl_window class and then derive win32_gl_window, sdl_gl_window etc. from that. We could have separate files, each containing a platform specific implementation of gl_window and use a macro to control which one gets included. We could implement each platform specific implementation in its own namespace, i.e. win32::gl_window, sdl::gl_window, use a macro to control which one gets included and then typedef it into our main namespace (typedef win32::gl_window gl_window;). The first is probably better in terms of code-reuse but means explaining inheritance and probably pure virtual functions. I'm drawn to the third, which is slightly more complex than the second but is slightly kinder on inexperienced programmers (accidentally including two or more files would lead to obscure duplicate definition linker errors).They are all better ideas than #ifdef-ing everything, i'm just worried we are losing the essence of NeHe, the keep it simple approach. We have to remember that people learning the Win32 code aren't going to be interested in the SDL and the two sets of code and perhaps tutorials will probably be in separate packages. The only reason I said about keeping the class prototype the same was so that learners can put the function bodies side-by-side and go "Oh I see, so thats how SDL does that" and also so that in future we can write stuff that doesnt depend on either version of the code.Anyway just my thoughts, Luke.;Quote:Written by EnigmaThere are a few ways of achieving this. We could build an abstract gl_window class and then derive win32_gl_window, sdl_gl_window etc. from that. We could have separate files, each containing a platform specific implementation of gl_window and use a macro to control which one gets included. We could implement each platform specific implementation in its own namespace, i.e. win32::gl_window, sdl::gl_window, use a macro to control which one gets included and then typedef it into our main namespace (typedef win32::gl_window gl_window;). The first is probably better in terms of code-reuse but means explaining inheritance and probably pure virtual functions. I'm drawn to the third, which is slightly more complex than the second but is slightly kinder on inexperienced programmers (accidentally including two or more files would lead to obscure duplicate definition linker errors).Quote:Answer of KazadeThe only reason I said about keeping the class prototype the same was so that learners can put the function bodies side-by-side and go "Oh I see, so thats how SDL does that" and also so that in future we can write stuff that doesnt depend on either version of the code.I think the last method Enigma described, about typedef-ing the used namespace into our project is a simple way and easy to understand. This doesn't contradict the structure of using functions which are called the same.In my eyes it's probably the eaiest way of giving users the opportunity to use either SDL or Win32 and to understand both.And Kazade, I agree with you, Con-/Destructors have to be small and clean.[wink]And concerning the WndProc procedure: We probably should make use of the SetWindowLong-function because otherwise it's too confusing when linking from one Proc to another, and it'd be too hard to explain   ;Quote:Original post by KazadeQuote:Regarding cross-platform issues I would advise against the approach in Kazade's latest post as it means lumping a lot of implementation-specific detail together with little separation. Sutter and Alexandrescu advise:Quote:    Quote:    C++ Coding Standards, Item 16: Avoid macros    For conditional compilation (e.g. system-dependant parts), avoid littering your code with #ifdefs. Instead, prefer to organize code such that the use of macros drives alternative implementations of one common interface, and then use the interface throughout.There are a few ways of achieving this. We could build an abstract gl_window class and then derive win32_gl_window, sdl_gl_window etc. from that. We could have separate files, each containing a platform specific implementation of gl_window and use a macro to control which one gets included. We could implement each platform specific implementation in its own namespace, i.e. win32::gl_window, sdl::gl_window, use a macro to control which one gets included and then typedef it into our main namespace (typedef win32::gl_window gl_window;). The first is probably better in terms of code-reuse but means explaining inheritance and probably pure virtual functions. I'm drawn to the third, which is slightly more complex than the second but is slightly kinder on inexperienced programmers (accidentally including two or more files would lead to obscure duplicate definition linker errors).They are all better ideas than #ifdef-ing everything, i'm just worried we are losing the essence of NeHe, the keep it simple approach. We have to remember that people learning the Win32 code aren't going to be interested in the SDL and the two sets of code and perhaps tutorials will probably be in separate packages. The only reason I said about keeping the class prototype the same was so that learners can put the function bodies side-by-side and go "Oh I see, so thats how SDL does that" and also so that in future we can write stuff that doesnt depend on either version of the code.Anyway just my thoughts, Luke.I actually think this way Enigma suggested is easier to understand than any other way I can think of.  If the new user sees a base window class, and then three other classes in files called, win32_window, linux_window, sdl_window, etc..., They won't need to worry so much about how that specific implementation gets to the base class, rather just what the implementation they are interested in actually does.  That is just my opinion though.   ;  Actually, i've been thinking about this and I've changed my mind Enigmas idea with the namespaces is probably best, (I misunderstood what he was saying) coz then the only change in the code would be the using namespace sdl; or using namespace win32 etc.For some reason I read the suggestion as they were different classes in the same namespace, i dunno i'm having one of those days. [smile]