[comp.sys.mac.programmer] Writing C code that works with both MPW and LightspeedC

guido@cwi.nl (Guido van Rossum) (05/22/88)

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 <pascal.h>
	#define p2cstr PtoCstr
	#define c2pstr CtoPstr
	#else
	#include <Strings.h>
	#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 <pascal.h> #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 <QuickDraw.h>
	#ifdef LSC
	#include <WindowMgr.h>
	#include <EventMgr.h>
	...
	#else
	#include <Windows.h>
	#include <Events.h>
	...
	#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 <storage.h>
	#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)).

singer@endor.harvard.edu (Rich Siegel) (05/23/88)

An excellent posting, but I wish to make a comment to point (10)). To wit,
as of the version 2.15 libraries, realloc() is not "broken"; a call to
realloc will in fact move the block.

		--Rich


Rich Siegel
Quality Assurance Technician
THINK Technologies Division, Symantec Corp.
Internet: singer@endor.harvard.edu
UUCP: ..harvard!endor!singer
Phone: (617) 275-4800 x305

earleh@eleazar.dartmouth.edu (Earle R. Horton) (05/25/88)

In article <332@piring.cwi.nl> guido@cwi.nl (Guido van Rossum) writes:
>	Writing C code that works with both MPW and LightspeedC
...
>3) LSC wants some points passed by value to the toolbox;
>MPW passes all points by address.
...
>5) LSC needs Pascal strings as toolbox parameters,
>MPW needs C strings.

Referring to the MPW C 2.0.2 Manual, appendix H:

     The new MPW C header files contain declarations which allow you
to access the ToolBox routines in the same fashion as LightSpeedC and
Pascal, so that most functions can be called the same way they are
declared in Inside Macintosh.  The only substantial difference is that
a Pascal String is declared in LightSpeed as an array of char, while
in MPW it is a struct.  In MPW, the ToolBox calls for the C interface
are declared using the spelling in Inside Macintosh, while the calls
for the Pascal Interface are declared using all upper-case.  I have
found that using the Pascal interface exclusively results in smaller
code, code which is more easily portable between the two compilers,
and code which is easier to debug.  Debugging is easier because many
of the Pascal Interface calls are actually macro expansions of Trap
calls, and you get to see these Traps right in the middle of your code.

     I include the following header file in ALL of my MPW source which
uses the Macintosh Interface, and AFTER any other Macintosh-specific
header files.  It merely remaps the upper-case spelling of the Pascal
interface back to the Inside Macintosh spelling.  If you use this file
then you can ignore the differences implied by Giudo's points (3) and
(5), except for some minor caveats:

     a)  You MUST include the function declarations for the routines
         you use in MPW.  LightSpeedC has most of these built into the
         compiler, while MPW C gets them from the #include file.

     b)  MPW C doesn't allow you to pass constant longs to routines
         which want Points.  LightSpeedC allows this.

     c)  MPW C Str255 is a struct, while LightSpeedC Str255 is an 
         array.  A PASSPT() macro neatly sidesteps this issue when
         passing strings to the ToolBox, but does not resolve the
         problem of how to access the characters individually.

     d)  If you use this header file as I do, then you don't use ANY
         of the C Interface glue routines to the ToolBox.  (If you
         care about performance and code size, then you shouldn't want
         the C Interface, anyway.)

     I call this file "compat.h", put a copy of it in {CIncludes}, and
include it after all Macintosh interface #include files in the
MPW-specific part of my C program headers.  If there are any omissions
or mistakes, I would appreciate hearing about them (not bloody
likely).

Earle

/*
 * This FILE is intended for use with the MPW C 2.0 Interface files
 * which generate InLine code for routines that call the
 * ToolBox with points by value and strings as Pascal strings.
 * It should be included AFTER any Macintosh #include files.  See
 * Appendix H of the MPW C 2.0 manual for details.
 *
 * This file allows use of MPW C 2.0 standardized ToolBox calls using
 * the spelling from Inside Macinstosh, and not all upper case, as is used
 * in the MPW C 2.0 header files.
 *
 * If you like to pass C strings to the ToolBox, do NOT include this
 * FILE.
 *
 * Earle R. Horton.	Friday, January 22, 1988 11:23:07 pm
 */
#define NewControl	NEWCONTROL
#define SetCTitle	SETCTITLE
#define GetCTitle	GETCTITLE
#define DragControl	DRAGCONTROL
#define TestControl	TESTCONTROL
#define TrackControl	TRACKCONTROL
#define FindControl	FINDCONTROL
#define OpenDeskAcc	OPENDESKACC
#define FindDItem	FINDDITEM
#define NewDialog	NEWDIALOG
#define ParamText	PARAMTEXT
#define GetIText	GETITEXT
#define SetIText	SETITEXT
#define NewDialog	NEWDIALOG
#define GetVol		GETVOL
#define SetVol		SETVOL
#define UnmountVol	UNMOUNTVOL
#define Eject		EJECT
#define FlushVol	FLUSHVOL
#define Create		CREATE
#define FSDelete	FSDELETE
#define FSOpen		FSOPEN
#define OpenRF		OPENRF
#define Rename		RENAME
#define GetFInfo	GETFINFO
#define SetFInfo	SETFINFO
#define SetFLock	SETFLOCK
#define RstFLock	RSTFLOCK
#define GetFontName	GETFONTNAME
#define GetFNum		GETFNUM
#define LCellSize	LCELLSIZE
#define LNew		LNEW
#define NewMenu		NEWMENU
#define AppendMenu	APPENDMENU
#define SetItem		SETITEM
#define GetItem		GETITEM
#define InsMenuItem	INSMENUITEM
#define MenuSelect	MENUSELECT
#define EqualString	EQUALSTRING
#define RelString	RELSTRING
#define UprString	UPRSTRING
#define DIZero		DIZERO
#define NumToString	NUMTOSTRING
#define StringToNum	STRINGTONUM
#define SFPutFile	SFPUTFILE
#define SFPPutFile	SFPPUTFILE
#define SFGetFile	SFGETFILE
#define SFPGetFile	SFPGETFILE
#define IUDateString	IUDATESTRING
#define IUDatePString	IUDATEPSTRING
#define IUTimeString	IUTIMESTRING
#define IUTimePString	IUTIMEPSTRING
#define IUCompString	IUCOMPSTRING
#define IUEqualString	IUEQUALSTRING
#define DIBadMount	DIBADMOUNT
#define DrawString	DRAWSTRING
#define StringWidth	STRINGWIDTH
#define StuffHex	STUFFHEX
#define AddPt		ADDPT
#define SubPt		SUBPT
#define EqualPt		EQUALPT
#define PtInRect	PTINRECT
#define Pt2Rect		PT2RECT
#define PtToAngle	PTTOANGLE
#define PtInRgn		PTINRGN
#define StdText		STDTEXT
#define CreateResFile	CREATERESFILE
#define OpenResFile	OPENRESFILE
#define OpenRFPerm	OPENRFPERM
#define GetNamedResource	GETNAMEDRESOURCE
#define Get1NamedResource	GET1NAMEDRESOURCE
#define GetResInfo	GETRESINFO
#define SetResInfo	SETRESINFO
#define AddResource	ADDRESOURCE
#define GetAppParms	GETAPPPARMS
#define OpenDriver	OPENDRIVER
#define TEGetOffset	TEGETOFFSET
#define TEGetPoint	TEGETPOINT
#define TEClick		TECLICK
#define NewString	NEWSTRING
#define SetString	SETSTRING
#define GetIndString	GETINDSTRING
#define DeltaPoint	DELTAPOINT
#define ShieldCursor	SHIELDCURSOR
#define NewWindow	NEWWINDOW
#define SetWTitle	SETWTITLE
#define GetWTitle	GETWTITLE
#define NewCWindow	NEWCWINDOW
#define GrowWindow	GROWWINDOW
#define DragWindow	DRAGWINDOW
#define TrackGoAway	TRACKGOAWAY
#define FindWindow	FINDWINDOW
#define PinRect		PINRECT
#define DragGrayRgn	DRAGGRAYRGN
#define TrackBox	TRACKBOX


/* This is for programs which are designed to be compiled in either
 * LSC or MPW C and with the same usage for Str255.
 */
#define PASS_STR(a) &(a)
*********************************************************************
*Earle R. Horton, H.B. 8000, Dartmouth College, Hanover, NH 03755   *
*********************************************************************

guido@cwi.nl (Guido van Rossum) (05/25/88)

Thanks for the posting of "compat.h".  This is indeed the better
approach when porting from LSC to MPW.

>I have found that using the Pascal interface exclusively results in
>smaller code [...]

It depends on what you are doing.  If you are writing a library which
has an interface that's portable to non-Mac machines, you'll have to
convert C strings to Pascal anyway, so doing the conversion in the glue
might actually save space as soon as a routine is called more than
once.  This doesn't hold for Points, though.  (In fact I would probably
have your approach, had it been available in MPW 1.0... :-( ).
--
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

msurlich@faui44.UUCP (Matthias Urlichs ) (05/27/88)

In MPW C 2.0x, calls that use C strings (w/ glue code) are spelled normally
while Toolbox calls using Pascal string conventions are spelled all-
uppercase.

In MPW 3.0, calls that use the C glue will be spelled all-lowercase while
the Pascal calls (not needing glue) will spelled normally
(i.e. Inside-Macish).

Disclaimer: This was heard in passing on the Apple Developer's conference
in Frankfurt/Main, Germany, last Wednesday, and may not necessarily
be correct. Any confirmation? Anyone having a file for the Canon tool to
translate all my C sources?
-- 
Matthias Urlichs     CompuServe: 72437,1357  Delphi: URLICHS
Rainwiesenweg 9      Phone: +49+911-574180
8501 Schwaig 2       NetMail: m_urlichs@msn.rmi.de
West Germany              or: (r)eply and (h)ope

wrs@Apple.COM (Walter Smith) (05/29/88)

According to the little note distributed with the revised Tech Notes
last week, most of the...how shall I say...*inelegances* in MPW C 2.0
have been fixed in 3.0, including the glue situation.  DrawString()
generates inline code; drawstring() uses glue; DRAWSTRING() no longer
exists.  3.0 comes with Canon dictionaries for converting your old code.

- Walt
--
Walter Smith		Apple Computer			wrs@apple.com
			Special Projects		(408) 973-4015
Disclaimer:
Anyone who thinks I might be representing Apple Computer, Inc. in any
official capacity on Usenet, of all places, has a serious attitude problem.

dan@Apple.COM (Dan Allen) (05/31/88)

Yes, it is true: MPW 3.0 will have the new **feature** of the C compiler
using mixed case for true Pascal calling conventions, and all lower case
will supply the appropriate C glue for the C/P string stuff.

And yes it will require editing of your sources.  That's the bad news.

The good news is that you will not have to do it, because MPW 3.0 will
ship with the appropriate Canon files that will do all of the
conversions for you!  These files already exist and have been put to a
good test: converting the MPW Shell sources themselves.  Consequently,
the new versions of the Shell are built post-Canonized, and all is well.

So the change will not be a big deal.  It will cause a bit of minor
confusion between people working across MPW 2.0 and MPW 3.0, but for the
average project, a simple bringing forward of the sources will suffice
and the Canon stuff effectively discarded.

Dan Allen
Software Explorer
Apple Computer