Archived

This topic is now archived and is closed to further replies.

Abominus

Portable Design Architecture

Recommended Posts

Abominus    122
I''ve been thinking about game portability lately and I''m toying w/ the idea of using inheritence to help modularize this. Basically, every entity (class) is divided into separate portable and non-portable sub-classes and the non-portable class inherits from the portable class (since portable code need not know about non-portable code to run, but the reverse may not be applicable). eg class CFooPortable { // blah blah }; class CFooSystem : public CFooPortable { // blah blah }; I haven''t actually tried this out on a real project yet but has anyone actually used a similar design and what implications/ shortfalls does it have in practice? Your comments/criticisms please.

Share this post


Link to post
Share on other sites
Dean Harding    546
I''ve found in most cases that non-portable classes will need to almost completely re-written anyway (i.e. they don''t share much common code at all) so this method is sort of overkill. What you end up with is the "portable" section is basically just an interface class and the "non-portable" section is where everything is implemented.

My solution is that you just make all you non-portable classes have exactly the same interface, and then just include only the current platform''s implementation of this class.

For example, say you have a Window class, which implements the functionality needed for your game to display a window. You''d have two files, Window_Win32.cpp and Window_Linux.cpp (and whever other platforms you support). You''d also have a Window_Win32.h and Window_Linux.h, which is conditionally included by a common Window.h depending on what platform is being compiled. In Window_Win32.cpp you include everything to display a window with the Win32 API, and in Window_Linux.cpp you include everything to display a window using X-Windows. Note that you do actually need two different .h files as well, since the private data in the Window class will be different on both platforms (you''d have a HWND in windows, which isn''t need under X, for example).

It means you have to be careful that the interfaces are the same, but then there''s no overhead of virtual classes.


codeka.com - Just click it.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
I agree with Dean. His approach will also make the code easier to maintain.

quote:
(since portable code need not know about non-portable code to run, but the reverse may not be applicable)


This suggests to me that your understanding of portability might need upgrading. The difficulties with non-portable code will quickly become apparent during compilation rather than during run time. Take Dean''s example of the window handle - attempting to compile code with the HWND symbol on Linux for example - will throw a compiler error. The HWND symbol can certainly be replaced with "unsigned long" or a typedef, but after a few pages of supplying substitutes, I think you will find the tedium rather boring.

The other alternative is to use conditional preprocessor directives - #ifdef and the like - but that can quickly lead to some really messy code. Better to keep platform specific code in a separate page - IMO.

Share this post


Link to post
Share on other sites
Shannon Barber    1681
Well, whether or not is needs a linux handle or a window handle isn't a big deal...

      
namespace GUI
{
struct IWindow
{
virtual void Create()=0;
virtual void RunModal()=0;
//...

};

class CMSWindow : public IWindow
{
HWND m_hMSWnd;
};

class CKDEWindow : public IWindow
{
//What's KDE need?

void* m_pKDEWnd;
};

}//ns GUI



But you still need to use conditional compilation to decide which one to instance (and which ones to ignore from the code).

Using templates might be a superior method?
  
template <class TWindowTraits>
class window
{
};


The method Dean suggested will get you correct results much faster though. It will probably be quite a headache to rectify the different windowing systems into one interface.


Magmai Kai Holmlor

"Oh, like you've never written buggy code" - Lee

"What I see is a system that _could do anything - but currently does nothing !" - Anonymous CEO

Edited by - Magmai Kai Holmlor on December 15, 2001 10:41:51 PM

Edited by - Magmai Kai Holmlor on December 16, 2001 2:43:52 PM

Share this post


Link to post
Share on other sites
LilBudyWizer    491
The only way I could see that helping is if you could get it to flag errors. The only way I could see to do that is to pass a caller handle to every function. Then the non-portable class would inherit off the portable class. A portable method would take a portable caller and the non-portable method would take a non-portable caller. It sounds a bit screwy in the inheritance, but the logic is that a non-portable class is more restrictive in who can call it. It might let you catch a few errors earlier, but it sounds like a pain to pass "this" on every single function call.

Share this post


Link to post
Share on other sites
Houdini    266
Although I agree with Dean Harding I would take a slightly different approach. I would use class policies to implement platform specific code. Then you could use a typedef to implement the class with the proper platform:

typedef BaseTimer Timer;

What does this buy you over Hardings way (which is very similar)? For classes not every single function is platform specific, you can still implement those functions in the base class, then just include the platform specific parts in the policy classes. Thus no code duplication, which leads to major headaches when you find a bug or need to make a modification to that common code.

This link explains class policies and their uses (although they don''t mention platform specific code as a use): http://www.awl.com/cseng/titles/0-201-70431-5/alexandrescuch1.pdf


- Houdini

Share this post


Link to post
Share on other sites