Article 12353 of comp.lang.c: Relay-Version: version B 2.10.3 Pyramid OSx-3.0 85/11/15; site intelisc.UUCP Path: intelisc!omepd!mipos3!intelca!oliveb!pyramid!decwrl!hplabs!motsj1!mcdchg!clyde!watmath!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 Date-Received: 23 Aug 88 06:52:11 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 Article 12354 of comp.lang.c: Relay-Version: version B 2.10.3 Pyramid OSx-3.0 85/11/15; site intelisc.UUCP Path: intelisc!omepd!mipos3!intelca!oliveb!ames!mailrus!tut.cis.ohio-state.edu!osu-cis!att!cbnews!mark From: mark@cbnews.ATT.COM (Mark Horton) Newsgroups: comp.lang.c Subject: Re: Portable Code Message-ID: <739@cbnews.ATT.COM> Date: 29 Jul 88 15:06:44 GMT Date-Received: 23 Aug 88 06:53:09 GMT References: <1157@radio.toronto.edu> Reply-To: mark@cbnews.ATT.COM (Mark Horton) Organization: AT&T Bell Laboratories, Columbus Lines: 73 In article <1157@radio.toronto.edu> brian@radio.astro.toronto.edu (Brian Glendenning) writes: > >So I thought I'd ask the net for advice about how to write portable code. I >know the topic is incredibly broad, but I expect I'm not the only person on >the net who could benefit from some advice (e.g., people like myself who >aren't primarily programmers and don't know the lore and wisdom of >professionals). Professionals have to learn this stuff some way too, and up until now the only option is experience and the seat of your pants. You might be interested in an upcoming book I'm writing called "How to Write Portable Software in C". It covers most of these sorts of issues. It's from Prentice Hall and should hit the stores next spring or summer. >Some issues that could be addressed: > > 1) Byte order and type size differences. What is the best way for > dealing with these? What are the "gotcha"'s? In general, don't make assumptions about them. Things like int c; read(0, &c, 1); if (c == '\n') will work on a little endian machine like a VAX but fail on a big endian machine like a Sun. There are zillions of potential gotchas like this, all of which come from assuming something in the folklore but not in the manual. (You aren't supposed to read into ints, just into chars, if you intend the data type to be a char.) > 2) BSD/SysV/whatever differences. What assumptions are likely to > lead me into trouble? Zillions of them. Without the book, which lists the functions and rates their portability, your best bet is to get both a System V and a 4BSD manual and verify that everything you do is in both manuals. That is far easier said than done. A typical UNIX system manual will imply that everything in it is portable, including the local enhancements that are not present anywhere else. > 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? ??? There are several choices: #ifdef vax This is useful if you need to key on the hardware type. It's automatically defined by cpp. #ifdef SYSV Useful to key on the operating system. You have to define this with -D or #define. #ifdef DBM Keying on a particular feature or option you need, you might have several of these and allow each to be configured. You must define these yourself. #ifdef FIONREAD Keying on some constant in a header file that is an indicator of whether the feature you need is present. These are automatic but you can't always use them. In this case you might be using this ifdef to protect an ioctl(.. FIONREAD ..) > 4) Everything I've forgotten :-) It took a 350 page book to cover this, so it's hard to do in a Usenet message. I recommend buying the book, if you can wait. Mark Horton Article 12987 of comp.lang.c: Relay-Version: version B 2.10.3 Pyramid OSx-3.0 85/11/15; site intelisc.UUCP Path: intelisc!omepd!mipos3!intelca!oliveb!ames!haven!ncifcrf!nlm-mcs!adm!smoke!gwyn From: gwyn@smoke.ARPA (Doug Gwyn ) Newsgroups: comp.lang.c Subject: Re: How do a write portable programs? Message-ID: <8450@smoke.ARPA> Date: 8 Sep 88 04:59:35 GMT Date-Received: 14 Sep 88 09:56:18 GMT References: <1056@nmtsun.nmt.edu> Reply-To: gwyn@brl.arpa (Doug Gwyn (VLD/VMB) ) Organization: Ballistic Research Lab (BRL), APG, MD. Lines: 44 In article <1056@nmtsun.nmt.edu> warner@hydrovax.nmt.edu (M. Warner Losh) writes: >How do I write program that are easily protable when I HAVE TO use the >system calls (be they setitimer() or lib$init_timer() or int21()...). Is >there a good and easy way that I can write my programs so that most of the >code never has to be touched when I port? What is the best way of handling >system dependent routines? Assume the whole world is UNIX and then write >UNIX system call emulation on those that aren't? Or should I write >routines that will do some functions (say turn off echo, but that is a bad >example) regardless of how that function gets done. Should I use zillions >of #ifdefs everywhere? or should there be files that contain source code >for only one system? There is one basic answer to all these questions: Encapsulate system dependencies in a set of interface modules, where you have carefully designed the interface to be as general as possible while providing the necessary functionality to support your application(s). For example, even though some systems support record locking, others don't, so if you need data integrity while updating records in a file see if you can arrange things so that whole-file locking will work acceptably. Then implement a simple lock/unlock file interface that does whatever is necessary to lock an entire file. In a single-user environment, these will be no-ops, in an old UNIX environment, you can probably use the "link to the file" kludge, and a 4BSD or SVR2.2 or later environment provides direct kernel support for file locking. The important point is that your applications proper contain "universal" calls to the appropriate interface routines; when porting, only the internals of the interface routines (which should be kept in separate source files) should require revision. This is just an extension of the "C library" approach, and it works quite well provided that the system interface is well designed. It doesn't take many #ifdefs to make one's code virtually unreadable, and one has the problem of properly parameterizing them, which is not as easy as one might think due to the existence of a variety of "hybrid" environments. (For example, a "SYSV" parameter might not discriminate sufficiently on a mostly-4BSD system with some SYSV features added, or vice-versa). By the way, associated with the "universal" system interfaces are normally one or more headers that declare the functions, define constants, etc. You can use such a header to accommodate the lack of a "void" type, and so forth, but don't overdo it or nobody except possibly you will be able to read your application's source code.