Path: utzoo!utgpu!radio!brian From: brian@radio.toronto.edu (Brian Glendenning) Newsgroups: comp.lang.c Subject: Portable Code (summary) Message-ID: <1159@radio.toronto.edu> Date: 28 Jul 88 18:01:07 GMT Reply-To: brian@radio.astro.toronto.edu (Brian Glendenning) Organization: Radio Astronomy, Dept. of Astronomy, U. of Toronto, Canada. Lines: 126 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@amsdsg (Ray Ryan) henry@utzoo.uucp (Henry Spencer) rsalz@pineapple.bbn.com (rich $alz) msb@sq.com (Mark Brader) proxftl!bill (T. William Wells) flaps@dgp (Alan J Rosenthal) chip@vector (Chip Rosenthal) jim@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 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@radio.astro.toronto.edu Radio Astronomy, U. Toronto UUCP - {uunet,pyramid}!utai!radio!brian +1 (416) 978-5558 BITNET - glendenn@utorphys.bitnet