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