- Individual Compilers
- Individual Platforms
- Platform Idiosyncrasies
- Wrapping Up
Platform Idiosyncrasies
With all this talk of keeping things simple, you might think that cross-platform design means coding to the lowest common denominator. While this approach might be tempting, it's far from best practice. Every platform has its own strengths and weaknesses. Not taking advantage of the strengths is not only a sad practice, but ultimately will cast your software into mediocrity, outshined by developers who go the extra mile to make sure that every bell and whistle is used to its utmost.
So beneath that abstraction layer will have to be one heck of a set of specifics. Consider system-administration software that must function equally well on a platform with no GUI (or with the GUI available but disabled), versus a platform with a GUI. The abstraction function might simply be DisplayQuery(), which then calls further layers as follows in pseudocode:
DisplayQuery(...) { if(GUI) { DisplayGUIQuery(...) { if (this is a Windows machine) { call the Windows library CreateDialog() function; } else if (this is a Mac) { call the Mac library's dialog creation function; } else if (this is a UNIX box) { call the X Window System's dialog creation function; } } } else if (text) { DisplayTextQuery(...) { if (this is an MS-DOS machine) { call an MS-DOS compiler's text dialog creation function; } else if (this is a UNIX box) { call a custom text dialog creation function written in ANSI C; } } }
As you can see, the code design starts to get heavily nested. However, if you employ a small host of programmers who are especially good with a particular platform, this overhead design allows those coders to focus on what they do best, while the GUI specialists can focus on the GUI design, and all of your other generalists and specialists can focus on their own specialties. This kind of modularity also allows you to purchase or license prewritten platform-specific components to save you a considerable amount of work. If you're a PC-based developer who knows little about the world of Macs, using a code library written by a Mac guru can save you from having to get intimate with the internals of Apple's operating system.
Where idiosyncrasies come more heavily into play is in platform strengths and weaknesses, and in the little ways in which different operating systems and hardware architectures handle things. For example, modern Windows implementations use threading, and UNIX has used processes since before the stone age. Both of these approaches allow for multitasking. To make things even more interesting, threading was also introduced to various flavors of UNIX, and the latest Linux kernel has taken Linux threading support to a whole new level that's worth looking into. This means that when dealing with Linux, you have to look at the best way to use threading and processes in conjunction.
Just imagine trying to approach these issues all in one program but without an abstraction layer!
Another major difference between Windows and UNIX/Linux is socket access. Linux and UNIX allow you to work down to the raw socket level. Microsoft Windows is designed to prevent this access. The difference is in some ways an issue of security. In general, UNIX and Linux allow you much lower-level access to hardware, kernel (at least in the open source arena), and more. Some people look at it as the difference between BASIC and C. C, UNIX, and Linux all give you plenty of rope to hang yourself with, but, boy, look at what you can do once you really know your stuff.
You might desperately wish that you had some platform-specific features available on other platforms. For example, many game and multimedia programmers are in love with Microsoft's DirectX. DirectX provides abstraction for graphics, sound, and input devices. The closest you can get to this in the multiplatform programming realm is OpenGL, which is popular in many circles, but only handles graphicsthe equivalent of the Direct3D component of DirectX.
So what's a programmer to do? Again, make the most of your abstraction layer, but also make the most of each platform's capabilities. In this case, you might take the DirectX API and clone it to the other platforms. For example, take every DirectX function call you're interested in using and create one for Mac OS X, Linux, FreeBSD, and so on, with the same name and argument typesin a way, you're building a second abstraction layer beneath the first. You might even stick with OpenGL for the graphics part if it does what you need to do very well, and only clone the input device and sound components. Many games that support both Direct3D and OpenGL do exactly this, abstracting the graphics code at a higher level. From then on, you'll be able to reuse your secondary multimedia abstraction layer in other projects, and develop it separately from the main code base if you want.
Another area that many people might want to clone comes from the Macintosh OS X world: AppleScript's ability to automate what happens in the GUIalthough various languages under other operating systems such as Linux can help as well (look into TCL/TK, for example). AppleScript also interacts with other scripting languages, anything from Perl to shell scripts (the UNIX equivalent of the batch file). Match AppleScript with a cron joba UNIX feature that lets you schedule programs to run, with what commands, and whenand you can do some really interesting things.
Many secondary abstraction layers are available in the open source world. If you consider that many open source projects begin by someone "scratching an itch," meaning that they want or need something and so start working on it, this fact makes sense. After all, most people in open source have been using computers for years. They have no doubt used many different platforms and all sorts of platform configurations, and develop favorite features along the way. One day, they decide they can no longer live without that feature, and a new open source project is born.