Portable Code (summary)

Brian Glendenning brian at radio.toronto.edu
Fri Jul 29 04:01:07 AEST 1988


A week or so ago I asked for advice on how to write portable C code. This is
the promised summary. If you would like me to send along the unedited messages
(including Henry Spencer's 10 commandments for C programmers) I'd be happy to
do so. In order to save net bandwidth I've edited this down pretty hard, maybe
too hard.

This message is based on the responses of the following people (thanks!):
     ray at amsdsg (Ray Ryan)
     henry at utzoo.uucp (Henry Spencer)
     rsalz at pineapple.bbn.com (rich $alz)
     msb at sq.com (Mark Brader)
     proxftl!bill (T. William Wells)
     flaps at dgp (Alan J Rosenthal)
     chip at vector (Chip Rosenthal)
     jim at dandelion.ci.com (Jim Hurt)


Leading >'s are the relevant question from my initial message, followed by a
summary of the responses. Errors are to be attributed to my misunderstanding,
not to the above respondents.

>        1) Byte order and type size differences. What is the best way for
>           dealing with these? What are the "gotcha"'s?


Byte order problems are most serious in networks. Other things to watch for
are multicharacter constants (e.g. don't use int x='ab').

Encapsulate size information in typedef's (e.g. typedef short WORD16).  Be
careful in printf statements, %d is not used for longs. It is best to cast to
long and use %ld if the sizes are hidden in typedef's. To avoid assuming a
particular size for a variable you can use bit expressions like x |= ~7 rather
than x |= 0xFFF8 and assuming the variable is 16 bits long.

It is safe to assume char is at least 8 bits, short and int at least 16, and
long at least 32, and that the unsigned types are the same length as the
signed types. You cannot assume that char is signed or unsigned. You should
avoid mixing signed and unsigned types in arithmetic or compare operations
unless you know there are no negative values.

You must of course be careful of function arguments, especially constants.
You must not assume pointers can be freely converted to integers. NULL (0)
must be cast if it is a function argument. Don't write into one member of a
union and read from another that has a different type.


>        2) BSD/SysV/whatever differences. What assumptions are likely to
>           lead me into trouble?

tty mode settings and esoteric library routines and system calls will cause
problems. It will often not be possible to write common code, and two versions
will be required.

Many machines (e.g. Suns) have mixed environments, where you can, e.g., use
memcpy instead of bcopy (and memcpy is the ANSI mandated function).

>        3) Source code management: what's the best way to maintain codes that
>           run on a variety of machines. #ifdef MACHINE_TYPE? Never or rarely 
>           use #ifdef, edit makefiles? ???

Most agreed that it was better to #ifdef on specific characteristics then to
#ifdef on machine type. For example, do not do:
	#ifdef	BSD
	#define strchr index
	#define strrchr rindex
	#endif	/* BSD */
but instead do:
	#ifdef USES_INDEX
	#define strchr index
	#define strrchr rindex
	#endif	/* USES_INDEX */

It was also widely believed that #ifdef's should be kept to a minimum since
they can make management awkward. For things that are very different (e.g. 
networking) it is better to use a consistent internal interface and build
different libraries for each interface.

It is helpful to have a config.h file that contains "all" the #ifdef
statements, and to keep the Makefile the same for all machines. 


>        4) Everything I've forgotten :-)

Read and follow Henry Spencer's 10 commandments for C programmers.  Buy a copy
of "Portable C and UNIX System Programming" by J.E.Lapin.

Don't write	#define MAC(xx) "xx"
which gives different results on different systems.  There's no portable
way to write a macro MAC such that MAC(k) would expand to "k" or 'k'.

Varargs.  There is no portable way to define a function that takes a
varying number of arguments.  If you try, you will at best land yourself
in a bunch of #ifdefs.  Better to design your functions to that each one
takes a fixed number of arguments.

Keep the significant parts of at least your external variable names short.


And finally, Jim Hurt <jim at dandelion.ci.com> sent me some general meta-rules.
I just include his points here, his rationales are included in the unedited
file I'll send out on request.

1.  Determine what computer/system combination is preferred by the people
actually generating the code.  Under no circumstances allow them to generate
code on that machine.

2.  Never do your code development on a machine made by Digital Equipment
Corporation.  These machines should be the first machine that your code gets
ported to.

3.  Select a language that has an ANSI standard, then use copies of that
standard as the programming language manual for use by your coders.  Do not
let your coders have access to the language manual provided by your computer
supplier.

4.  Carefully isolate your machine dependent code in a few very
carefully designed procedures.


I suggest further discussion, if any, now be directed at the net. Thanks again.


-- 
Brian Glendenning                INTERNET - brian at radio.astro.toronto.edu
Radio Astronomy, U. Toronto          UUCP - {uunet,pyramid}!utai!radio!brian
+1 (416) 978-5558                  BITNET - glendenn at utorphys.bitnet



More information about the Comp.lang.c mailing list