Jump to content
  • entries
  • comments
  • views

Platform compatability

Sign in to follow this  


I want to talk about platform compatability as it plays a fairly important part in my coding life. I use cross-platform libraries and try to write code that's as portable as possible. This is why I choose OpenGL over DirectX, PhysFS over the Win32 file API and SDL/GLFW over writing window/input code myself.

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...
Sign in to follow this  


Recommended Comments

Does anyone have any comments on this way of coding?

It sounds cleaner/tidier than my methods, I can comment on that much.

The only thing that I really don't like about similar architectures is when they have lots of #ifdef/#endif blocks to mark out the platform specific stuff. Something like:

#ifdef WIN32

#include <Graphics_D3D.h>
#include <Graphics_OpenGL.h>

is fine, but i've seen some examples where it's gone a bit crazy. Hard to read code makes for easy to introduce errors.

I'm also a little weary of the recompile for each platform part. I'm not sure theres an elegant way around it (each OS has a different binary format).

I'm a big fan of the modular architectures that use dynamic binding - you just need to drop in a new "something.dll" or "something.so" and it'll extract the interface it wants and no NOTHING else about it. But at some point you have to have some sort of kernel/"loader" in the native binary format..

In fact, I'd love to try that... where you have a simple exe with your main() and a published LoadModule(name,fptr) type functionality. That way you could just do a:

typedef void (*game_core_fn)(int*,char*) GAME_CORE_FN;

int main()
LoadModule( "Game_Core", start );
(*start)( 12, "Hello, World" );

#ifdef WIN32

void LoadModule( char* name, GAME_CORE_FN fptr )
// Create the function ptr using Win32 dll functions


void LoadModule( char* name, GAME_CORE_FN fptr )
// Create the fptr using unix/linux functions


And if LoadModule() were accessible from the other libraries then they could, in turn, load any dependencies that they have.

I'll be quiet now. I'm rambling.

Share this comment

Link to comment
A very interesting post.

I've been working to port my own framework/engine to the Nintendo DS at my work during lunch hours. It previously worked on Windows, and a long time ago also on Linux (haven't check recently). So I've got quite some experience with this I feel.

I have the common .h file, but also a .cpp file with common stuff, and the following:

#ifdef NDS
# include "nds/window.cpp"
#elif WIN32
# include "win/window.cpp"

I structure my project with a lot of folders, more than I've ever seen anybody else use. So the os dependent part is under /os in my source tree, and it has a /nds and a /win beneath it. All platform specific files are in there.

Theoretically, this setup would allow me to compile for each and every platform with the same makefile (assuming it builds the to-build list from directory contents), but only changing a single, or possibly two defines.

This has worked quite well for me. I've been able to port what I had on the pc to the DS in less than 20 hours. Beyond that, I still needed to fix the renderer for the DS, but it was compiling and running fine.

jollyjeffers: How would that work? I've always assumed that the .dll/.so would have to be recompiled per platform anyway, so you could as well recompile the program itself.

Share this comment

Link to comment
jollyjeffers: How would that work? I've always assumed that the .dll/.so would have to be recompiled per platform anyway, so you could as well recompile the program itself.
I hadn't really thought it through fully so it probably wouldn't work [headshake]

I'm sure it made sense when I was typing, but on retrospect it doesn't seem to make much sense. I'll go give it some thought while I cook my dinner...


Share this comment

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!