Here's how one developer supports more than 100 platforms
How many different combinations of operating systems and hardware platforms are used today? Fifty, a hundred--does anyone really know? How many are in the mainstream? DOS, Windows, NT, OS/2, Solaris, UNIX--Intel, Motorola, DEC, SunSPARC, IBM. With all of these choices, how are you to develop truly portable applications?
The good news is that if an application is approached correctly and with foresight, writing portable code does not have to be a chore. In this article, we'll discuss coding strategies for developing truly portable database applications. In doing so, we'll focus on the strategies you can implement to ease the movement of code and data between computer platforms. The topics include code portability, function wrappers, size and alignment of data objects, binary word order, and true multiplatform portability. All of the techniques we'll cover here are real world--they're what FairCom uses to make its c-tree Plus File Handler highly portable. c-tree Plus is a C-function library of database calls designed from the ground up with portability in mind. The c-tree family has been ported to well over 100 platforms ranging from Cray supercomputers to embedded systems and virtually all machines in between.
Code Portability
The way you organize modules which comprise your application can greatly affect the time required to port it. We suggest explicitly organizing your application modules into two sets: one that is system independent and one that is system dependent. For example, in c-tree Plus, about 98 percent of the code resides in system-independent modules which are not changed when we port from platform to platform. Not one line of code in these modules has to be touched. The remaining code--the system-dependent modules--contains those aspects of c-tree Plus which depend on system specifics. For c-tree, virtually all the system-specific code relates to low-level file operations.
To achieve this degree of separation, certain sections of a system-independent module may depend on a configuration setting in a system-dependent header file. However, these dependencies should reflect generic concepts, not platform-specific issues. In c-tree Plus for instance, there are #ifdef
s in the system-independent modules which depend on the word order of binary values. Each system-dependent configuration header specifies the type of word order found on that platform; then the system-independent code need only have #ifdef
s for the word-order choices, not for each platform.
To minimize unexpected problems when moving your C source code from one platform to another, it is advisable to utilize a well-defined set of typedef
s for the basic computational objects as well as for application-specific objects. For example, in c-tree Plus we use three different typedef
s for integers: COUNT, LONG, and NINT. They are, respectively, 2-byte integers, 4-byte integers, and the platform's natural integer. (Of course, we also support unsigned versions of these integers.) Then on any platform, c-tree can always rely on a COUNT to be two bytes and a LONG to be four bytes. This is implemented in a manner typical of our portability strategy: Default typedef
s are supplied in a system-independent header module, and an optional entry in a system-dependent module can override the default. For example, default typedef
s like Example 1 are found in a system-independent header file. In those few platforms where a short int
is not two bytes or a long int
is not four bytes, these typedef
s can be specifically coded in the system-dependent header file, and the #ifndef
will be false in the system-independent module.
Example 1: Default typedefs like this are found in a system-independent header file.
#ifndef INTEGER_OVERRIDE typedef short int COUNT; typedef long int LONG; #endif