Path: utzoo!attcan!uunet!mcvax!guido From: guido@cwi.nl (Guido van Rossum) Newsgroups: comp.sys.mac.programmer Subject: Writing C code that works with both MPW and LightspeedC Message-ID: <332@piring.cwi.nl> Date: 21 May 88 19:55:33 GMT Reply-To: guido@cwi.nl (Guido van Rossum) Organization: The Royal Society for Prevention of Cruelty to Amoebae Lines: 187 I mentioned I used some macros to writing code that would be portable between the two major C compilers for the Mac. Already I received two letters asking me to post my macros; I decided to make it a little essay. Enjoy. If you know of more trouble areas, please post! (The actual macro file I use is of little use since it is interspersed with more-or-less project-dependent definitions and other things of which I am not too proud.) Guido van Rossum, Centre for Mathematics and Computer Science (CWI), Amsterdam guido@piring.cwi.nl or mcvax!piring!guido or guido%piring.cwi.nl@uunet.uu.net ------------------------------------------------------------------------------ Writing C code that works with both MPW and LightspeedC Guido van Rossum, CWI, Amsterdam (guido@cwi.nl) 0) How to find out which compiler is used? I use the following ugly code, snugly put away if a file "configure.h". #ifndef unix #ifndef MSDOS #ifndef LSC #ifndef macintosh #define LSC #define macintosh #else #ifndef MPW #define MPW #endif #endif #endif #endif #endif Now I can put my LSC-dependent code between #ifdef LSC / #endif, and my MPW-dependent code between #ifdef MPW / #endif. It doesn't matter if you include this more than once, and it should work on Unix, MS-DOS and the Mac. The basic knowledge here is that MPW defines 'macintosh' while LSC doesn't. Note that if defines macintosh if it finds the compiler is LSC, since you may have code that is mac-specific but not compiler-specific (if your code also runs under Unix or MS-DOS). 1) LSC int == short, MPW int == long. Use short, int and long peoperly. That is: use long wherever the Pascal interface has LongInt; declare variables as short if their address is passed to a toolbox routine; but use int for most cases. I don't believe in declaring all local variables as short; for parameters, declaring them as short doesn't even help, since all parameters passed are implicitly widened to int anyway (read K&R). 2) LSC pointer difference is of type long. When subtracting pointers, cast the result to (int) when passing it directly to a function requiring an int. (Or, if you are defining the functions, and expect differences >32K, declare the parameter as long.) 3) LSC wants some points passed by value to the toolbox; MPW passes all points by address. I define the macro PASSPT: #ifdef MPW #define PASSPT & #else #define PASSPT /**/ #endif Example of use: if (PtInRect(PASSPT p, &r)) { ... } 4) MPW has quickdraw globals in a struct named 'qd', LSC defines each global separately. I define the macro QD(): #ifdef MPW #define QD(name) qd.name #else #define QD(name) name #endif Example: SetCursor(&QD(arrow)); 5) LSC needs Pascal strings as toolbox parameters, MPW needs C strings. Personally, I keep all strings as C strings, and convert them to Pascal as needed. I pass C strings to the toolbox with a macro that does nothing for MPW, but converts C to P for LSC: #ifdef MPW #define PSTRING(str) (str) #else #define PSTRING(str) pstring(str) /* Really put this in some other file: */ char *pstring(str) char *str; { static char buf[256]; char *strcpy(), *CtoPstr(); return CtoPstr(strcpy(buf, str)); } #endif Example: SetWTitle(w, PSTRING(title)); Warning: this doesn't work if you are passing TWO strings in the same call, since the static buffer would be overridden. 6) The functions to convert between C/Pascal strings have different names. #ifdef LSC #include #define p2cstr PtoCstr #define c2pstr CtoPstr #else #include #define PtoCstr p2cstr #define CtoPstr c2pstr #endif 7) LSC is stricter with function pointers. Always call like (*fp)(parameters) if fp is a function pointer. 8) LSC needs glue to call function pointers to pascal functions. I haven't found a good trick for this; use #ifdef LSC /* Assume #included */ CallPascal(arg1, arg2, ..., argn, fp); #else (*fp)(arg1, arg2, ..., argn); #endif 9) And, of course, the Mac #include file names are different. I put most #include statements together in a header file containing stuff like this: #include #ifdef LSC #include #include ... #else #include #include ... #endif BTW, note that MPW defines the traps in the header files, while LSC has them built into the compiler. As a consequence, you need to include a header file in MPW even if you are only using only a trap from it; in LSC you only need to include the header if you are using data structures or constants defined in it, or functions not returning int. 10) Beware of malloc and realloc. LSC's realloc is broken. I have written code to replace all of LSC's "storage" library to fix it by using locked handles instead of pointers. Also, LSC has a special malloc to allocate blocks > 64K. In MPW one malloc fits all (plus that it's more efficient if you are allocating many small blocks). You can use: #ifdef LSC #include #else #define mlalloc malloc #define clalloc calloc #define relalloc realloc char *malloc(), *realloc(), *calloc(); #endif Beware of programs that use malloc without declaring it! LSC will think it returns an int, and not complain because most malloc calls are followed by a cast to some non-char pointer type; the cast from int to pointer will sign-extend the (short) int, which guarantees instant bombs when you use it... PS) Above I used #ifdef LSC / #else / #endif to make my examples marginally shorter. In practice I use #ifdef LSC / #endif / #ifdef MPW / #endif; this makes it easier to convert the sources if a third compiler brand with again different needs would spring into existence. Extra) LSC knows about ANSI function prototypes. MPW also, but I don't think it checks them; it just accepts the syntax. This means you can leave them in if you write code only for MPW and LSC. If you want code that's portable to Unix and other non-ANSI compilers, you can define the following two macros: #ifdef macintosh #define ARGS(arglist) arglist #define NOARGS (void) #else #define ARGS(arglist) () #define NOARGS () #endif Now you can declare functions as follows: char *malloc ARGS((unsigned int)); void foobar NOARGS; I tend to use this even in non-Mac code, as a way of documenting the parameter conventions, and in expectance of an ANSI C compiler "real soon now". It is also handy if you plan to use the same code with C++ (there you'll have to define NOARGS as (), not (void)).