I want to answer this here:-
Original post by Ravuya
I try to make code as portable as I possibly can, as a rule of thumb. Usually all I have to do to port between Linux, OS X and Windows is change a few lines of header directives the first time I build, and then set up an #ifdef for successive builds. Easy!
That's how I do it too - nice and easy! If you've never written for another platform but feel like you would like to, try some of this stuff - it can't hurt [grin].
My system is set up so that any specific graphics or platform files are implemented in their own .cpp, with subtle differences being tied up in the #ifdef blocks you suggested. Compiling for another platform/API is as 'simple' as setting a new define and running the platform's Makefile to compile the relevant source.
Right now, my directory looks something like:
.. etc ..
.. etc ..
I'm thinking about splitting out the various platform specific stuff into platform subdirectories to ensure it's not as cluttered, but you should be able to tell how things are separated out. All of the public interface functions are declared in the main, non-platform specific header (eg: gfx_graphics.h) and implemented in the platform specific .cpp file. For GMGX I am coding in a C-like style, so it's fairly easy to write code in this manner. If you were using object-oriented C++, you'd probably define your interface as an interface class and hook up a static 'Create' function to ensure the correct platform's implementation is used.
To port GMGX from Windows to the GP2X I went through and added a new system (sys_system_SDL.cpp), a new input system (in_input_SDL.cpp) and a new graphics system (gfx_graphics_SDL.cpp and tex_texture_SDL.cpp). Everything else in the code is forced to go through these abstract functions, so it's impossible to try and render a sprite 'manually' or update texture data by manipulating the data. Any data that comes from this platform/API specific code is either held in a common format (eg: my collision mask data derived from the texture bitmap) or is inaccessible outside of the system itself; this is achieved by providing handles to the data instead of pointers. A handle is specific to the system it's allocated from, so in one implementation it could be a masked pointer and in another it could be an offset into an internal array of real pointers. The idea is that it masks data from the user and forces them to go through the API - this is, in my opinion, A Good Thing (TM).
I like this way of coding, even if you're not planning on porting to another platform. For one, it helps encapsulate everything properly so that nothing should (in theory) get altered outside of your API implementation. Secondly, it allows you to rip out and change the whole implementation of subsystems very easily. If you wanted to write a DirectX renderer, for example, you could do so fairly easily (hopefully). An example of this is the filesystem abstraction I'm using. Originally I was using the C file API wrapped in a common interface (fs_* functions). I was able to tear this out and implement a filesystem based on PHYSFS in very little time just by coding a new implementation. If I ever needed to swap back to a C-file system I could just rebuild my source with the old file instead of PHYSFS.
Does anyone have any comments on this way of coding? I'd be interested in hearing your own experiences of developing in this manner...