tags:

views:

242

answers:

8

For a project at university I need to extend an existing C application, which shall in the end run on a wide variety of commercial and non-commercial unix systems (FreeBSD, Solaris, AIX, etc.).

Which things do I have to consider when I want to write a C program which is most portable?

+2  A: 

It is a very long list. The best thing to do is to read examples. The source of perl, for example. If you look at the source of perl, you will see a gigantic process of constructing a header file that deals with about 50 platform issues.

Read it and weep, or borrow.

bmargulies
When I read perl, I weep.
Seth
That's an entirely different problem. This is the source of perl. It should make you howl.
bmargulies
+1  A: 

Try to stick to ANSI C.

Mustapha Isyaku-Rabiu
This is surprisingly little help, because ANSI C does not address a whole host of things that many non-trivial programs want to do. You know, like talking to the network and accessing filesystems in a general way. See http://stackoverflow.com/questions/1257923/why-is-it-difficult-to-write-portable-c-programs
dmckee
+1  A: 

The list may be long, but it's not nearly as long as also supporting Windows and MSDOS. Which is common with many utilities.

The usual technique is to separate core algorithm modules from those which deal with the operating system—basically a strategy of layered abstraction.

Differentiating amongst several flavors of unix is rather simple in comparison. Either stick to features all use the same RTL names for, or look at the majority convention for the platforms supported and #ifdef in the exceptions.

wallyk
+6  A: 

The best advice I can give, is to move to a different platform every day, testing as you go.
This will make the platform differences stick out like a sore thumb, and teach you the portability issues at the same time.

Saving the cross platform testing for the end, will lead to failure.

That aside

  • Integer sizes can vary.
  • Compilation options can vary.
  • include file names can vary.

It is generally a good idea to set your compiler warning level up as high as possible, to see the sorts of things the compiler can complain about.

EvilTeach
+1 for high compiler warning levels.
Dan
+2  A: 
  1. Use atleast two compilers.
  2. Have a continuous build system in place, which preferably builds on the various target platforms.
  3. If you do not need to work very low-level, try to use some library that provides abstraction. It is unlikely that you won't find third-party libraries that provide good abstraction for the things you need. For example, for network and communication, there is ACE. Boost (e.g. filesystem) is also ported to several platforms. These are C++ libraries, but there may be other C libraries too (like curl).
  4. If you have to work at the low level, be aware that the platforms occasionally have different behavior even on things like posix where they are supposed to have the same behavior. You can have a look at the source code of the libraries above.
Amit Kumar
+1  A: 

One particular issue that you may need to stay abreast of (for instance, if your data files are expected to work across platforms) is endianness.

Numbers are represented differently at the binary level on different architectures. Big-endian systems order the most significant byte first and little-endian systems order the least-significant byte first.

If you write some raw data to a file in one endianness and then read that file back on a system with a different endianness you will obviously have issues.

You should be able to get the endianness at compile-time on most systems from sys/param.h. If you need to detect it at runtime, one method is to use a union of an int and a char, then set the char to 1 and see what value the int has.

Phil Booth
+1  A: 

Continually refer to the POSIX standards for any library functions you use. Parts of the standard are ambiguous and some systems return different styles of error codes. This will help you to proactively find those really hard to find slightly different implementation bugs.

Harvey
+2  A: 

I used to write C utilities that I would then support on 16 bit to 64 bit architectures, including some 60 bit machines. They included at least three varieties of "endianness," different floating point formats, different character encodings, and different operating systems (though Unix predominated).

  1. Stay as close to standard C as you can. For functions/libraries not part of the standard, use as widely supported a code base as you can find. For example, for networking, use the BSD socket interface, with zero or minimal use of low level socket options, out-of-band signalling, etc. To support a wide number of disparate platforms with minimal staff, you'll have to stay with plain vanilla functions.
  2. Be very aware of what's guaranteed by the standard, vice what's typical implementation behavior. For instance, pointers are not necessarily the same size as integers, and pointers to different data types may have different lengths. If you must make implementation dependent assumptions, document them thoroghly. Lint, or --strict, or whatever your development toolset has as an equivalent, is vitally important here.
  3. Header files are your friend. Use implementaton defined macros and constants. Use header definitions and #ifdef to help isolate those instances where you need to cover a small number of alternatives.
  4. Don't assume the current platform uses EBCDIC characters and packed decimal integers. There are a fair number of ASCII - two's complement machines out there as well. :-)

With all that, if you avoid the tempation to write things multiple times and #ifdef major portions of code, you'll find that coding and testing across disparate platforms helps find bugs sooner. You'll end up producing more disciplined, understandable, maintainable programs.

mpez0
+1 for point 4.
Steve Jessop
And I thougt the C standard libary is the same on every *nix system. So much for the 'standard'...
Helper Method
The C Standard library is the same. Many programs use things outside the C Standard, such as inter-process communication. Besides which, "most portable" probably extends past Unix system.
mpez0