Writing Cross-Platform Software: Getting Started

An explanation of microprocessor and compiler dependent byte order, data alignment and structure padding.

Michael D. Crawford

Michael D. Crawford
hotcoder@gmail.com

All Rights Reserved.

I'm For Hire!

Do you need a Cross-Platform Software Engineer?

Contents

Why Cross-Platform?

You might wonder what the point of reading an article on cross-platform development is. Do you program for Linux? Why would we want software on any platform but Linux? There are lots of reasons to write cross-platform code.

Cross-platform development can mean different things. Jon Watte of Be, Inc. says that "Portable, to some people, means it builds on at least two Linux distributions with various flavors of gcc." It is a spectrum that ranges from porting crusty old legacy code that can only build on one version of a particular compiler and run on one platform to porting an application between different major platforms, to simultaneous cross-platform development by coding to a platform-independent application framework.

Java is not really a cross-platform development environment; it is a platform in itself (this is also pointed out by Bjarne Stroustrup, which is readily able to run on almost any machine, but one in which platform specific features can be used only with great difficulty. It is my intention to focus on cross-platform development in C and C++, although I expect I will have more favorable things to say about Java later.

It would be especially helpful to increase the acceptance of free software if users could run their applications on any platform they wanted and exchange documents with users on other platforms, as is possible with the AbiWord word processor from AbiSource, which presently runs on Linux and other Unix variants, BeOS, and Windows, with MacOS and QNX versions in development. It is less likely that a whole company will adopt Linux than that a few geeks and power users will bring the OS in because of their personal preference; if they can be integrated into the company productivity system without need for clumsy and inaccurate file format translators then Linux will have greater acceptance, and ultimately less technical users will adopt Linux because of its many advantages.

With Linux starting to make inroads on the desktop, publishers of applications for Windows and MacOS are increasingly interested in shipping Linux versions of their products - for some it is a major strategic decision made to compete with Microsoft, as with Corel's WordPerfect. It can be pretty painful to perform such a port, but it can be done. Spellswell from Working Software is one of the earliest Macintosh applications that is still shipping (it was written on the 512k Mac with floppies and no hard drives, then later ramdisks, and spellchecked Word 1.0 documents), and was full of non-portable code, but I ported it to the BeOS and the OEM version is also available on Windows.

Why port? Besides the obvious benefit of your program running on more than one kind of computer, porting is good for code. The work required to make a single set of sources work for more than one platform increases its robustness, makes maintenance easier and improves the reusability of your code. Exposing your code to multiple compilers reveals coding errors in one compiler that are not detected in another.

For example, I did simultaneous development of a MacOS and Windows application using a cross-platform framework named ZooLib written by my friend Andy Green (more about it in a future column). I used the Metrowerks CodeWarrior Pro compiler, which runs on Mac and Windows. Various incarnations of the MetroWerks C++ compilers can build applications for:

  • 680x0 and PowerPC MacOS

  • Pentium Windows

  • PalmOS

  • QNX Neutrino with x86 and PowerPC

  • Nucleus PLUS with PowerPC, MIPS and NEC V8xx

  • PowerPC BeOS

  • Game consoles

It also supports Java development and the IDE can be used to drive gcc on Linux, Solaris and BeOS. Interestingly, MetroWerks publishes PowerPlant, the most popular application framework on the MacOS, but it is highly Mac-specific and shows no signs ever having cross-platform support - CodeWarrior for Windows supports Microsoft Foundation Classes.

I have found several code constructs which will build on Windows but not even compile on PowerPC, or work fine on PowerPC but not 68000 - sometimes due to my bug, sometimes due to a bug in the compiler. There is a benefit to the community in writing cross-platform code, in that it helps the compiler programmers improve their products if application developers report bugs they find in the development tools.

When Be, Inc. switched from building the BeOS on Pentiums with CodeWarrior to gcc, they found many instances of code that would build under one compiler but not the other, allowing them to improve the quality of their code. Us BeOS developers had the same experience building our applications. Even though Apple stopped providing low-level specifications to their new motherboards, so Be could no longer implement support for new models of Macintoshes, Be continued to develop and support PowerPC Macintoshes because it helped proof the Pentium code and uncover bugs in newly written code, and helped maintain the portability of the system.

Testing your application on different platform will ease debugging. Latent or unreproducible bugs on one platform often will become readily reproducible on another because of the different environment or layout of memory. Also you may be able to take advantage of platform-specific memory debugging tools such as SpotLight, BoundsChecker, Bounded Pointers for GCC or Purify and find bugs in code running on one platform that could affect all platforms.

Even if you only write code for Linux, it is to your benefit to aim for cross-platform compatibility. Besides building on Pentium with gcc, you could build gcc as a PowerPC or Alpha cross-compiler and have a friend test your binaries - or invest in a second machine with a different processor architecture for testing. For the best results, obtain a second, independently developed compiler that builds for your main development machine and build and test your code with that compiler periodically. I dislike like using Microsoft Visual C++ because of the MDI interface but test my Windows projects with it to do such verification.

If you work on a collaborative project, try to find at least one member for your team who writes on a platform with a different byte order. If you are on a programming team at a company, your programming team should be outfitted with at least one machine with a different byte order that is used in regular day-to-day development, and preferably there will be at least as many processor architectures as team members.

Even if you only desire your code to run on a Unix variant, there are many different processor architectures and compilers in use. You need to architect your code so that byte-order dependencies are handled properly, and you have to handle the processing of file and network data so that there is no dependency on processor or compiler dependent structure alignment.

Some cross-platform frameworks provide least-common denominator behaviour. An application cannot take advantage of special features of the platform it is running on - or have distinctive behaviour that is expected by users of particular platforms. For example, the javax.comm serial communications package for Java provided by Sun provides a method for enumerating the port names so they can be presented to the user for selection.

On a Windows machine the enumeration returns "COM1:" and "COM2:", and on a Mac the result is "Modem Port" and "Printer Port." But there was no provision in the javax.comm API for port icons; Macintosh serial ports are not labeled on the machine case with text - they have icons showing a telephone handset and a printer. A Java program will look out of place on the MacOS when compared to native Mac programs because it doesn't have the icons that are normally used for port selection.

Mac applications also have a menu bar across the top of the screen and usually do not have one in the document window, while most other platforms provide the menu bar in the document window. It is common for Windows applications to use the "Multiple Document Interface" in which document windows slide around inside a containing application window - the MDI interface generally causes a violent reaction to people who prefer other platforms; I would be completely unwilling to write Windows software if the development environments I use did not give me a choice between interface styles. It is important to support the native interface on the platform your application is running on.

This least common denominator approach is not necessary and I feel is wrong. I believe it is fully possible to write cross-platform code that has behaviour unique to each platform, and that takes advantage of special capabilities where they are available.

Vote for Us at the Programming Pages

Voting for GoingWare at The Programming Pages will encourage more people to read these articles.