Path: utzoo!utgpu!jarvis.csri.toronto.edu!cs.utexas.edu!uwm.edu!ogicse!orstcs!upas.CS.ORST.EDU!shepard From: shepard@upas.CS.ORST.EDU (Mark Shepard) Newsgroups: comp.lang.c Subject: Escaped Parenthesis and Portable Sign-extension (medium) Keywords: portability tricks-with-pointers oxymoron escaped-characters Message-ID: <16119@orstcs.CS.ORST.EDU> Date: 22 Feb 90 03:20:43 GMT Sender: usenet@orstcs.CS.ORST.EDU Reply-To: shepard@upas.CS.ORST.EDU (Mark Shepard) Organization: Oregon State University--Electrical & Computer Engineering Lines: 138 I have two questions about portability (forgive me if these are too simplistic but I'd like a second opinion). First, parenthesis: In a program I'm currently hacking on, the chars '(' and ')', when they appear as char constants, are escaped: '\(' and '\)'. As far as I know ( and ) have no special meaning, and in k&r C the escape is just ignored (right?). But "gcc" complained, and I was going to change all the \( and \) to ( and ) to keep it happy, but...wait a minute...SOMEBODY must have put those there for a reason?!? Is there a reason these are escaped? If I change 'em back to satisfy gcc, will this program break on some other system? What does ANSI say about escaping characters which don't need it? (undefined?) Okay, now the second thing: ( You're gonna think this is REALLY WEIRD, so please explain to me the "enlightened" solution. ) Problem: Say I have a variable X of some integer (char, short, int, long) type Xtype_t, and that I want to print out this variable with printf() in many places in my program. Now, I want this program to be as easy to maintain as possible, so I don't want to spread information about the actual type of Xtype_t all throughout the program via my printf() format statements, which have to specify the width of the integer-type which I'm passing. e.g.: typedef long Xtype_t; ... Xtype_t X; ... printf("%ld",X); If I later change Xtype_t to int or short and if the size of ints, shorts, and/or longs aren't the same, I'll have to change all the printf's. Yuck. Solution 1: Put a suitable printf-format string-constant or something in a .h file--change this when you change Xtype_t. #define print_Xtype(x) printf("%ld",x); typedef long Xtype_t; ... print_Xtype(X); This an similar schemes work, but I reject them because there so cumbersome...after all, that's what's so nice about printf! Solution 2: Cast everything to a long before printing. Since the size is unknown, we just force it to be a long and then print it as a long--so we don't have to change formats when we change the type: printf("%ld",(long)X); Okay, this is pretty good, but (now I'm going to change the problem :-) lets say I want to print X as both signed and unsigned (or in hex, which is always unsigned). If I do: printf("dec=%ld hex=%lx",(long)X,(long)X); if X is in fact smaller than a long and is a signed type, it will be sign-extended which will be fine if I print it out as signed, but if I print it as unsigned, I'll get something like this: /* ints are 16 bits, longs are 32 bits */ X=-2; ... printf("dec=%ld hex=%lx",(long)X,(long)X); output: dec=-2 hex=fffffffe instead of the correct output: dec=-2 hex=fffe ( Yes, I could probably screw around with field-sizes, etc, in printf, but that sort of defeits the purpose. ) My Solution: I wrote two macro/functions, UL() and SL(), which take any integer type and convert that type to a long with (SL) sign-extension or without (UL) sign-extension. Thus, I can write: printf("dec=%ld hex=%lx",SL(X),UL(X)); and get the correct result, regardless of how I change the actual type of X in some header file. Here are UL and SL: /**********************************************************************/ /* ulsl.h */ /* UL(x), SL(x) -- convert x to long-int, with or w/o sign-extension */ #define UL(x) _UL((void *)&(x),sizeof(x)) #define SL(x) _SL((void *)&(x),sizeof(x)) extern unsigned long _UL (_2( void * , size_t )); extern long _SL (_2( void * , size_t )); /* ulsl.c */ /* _UL -- convert any integer type to unsigned-long W/O sign-extension */ unsigned long _UL( p, s ) void * p; size_t s; { if (s==sizeof(char)) return (unsigned char)*(char *)p; if (s==sizeof(short)) return (unsigned short)*(short *)p; if (s==sizeof(int)) return (unsigned int)*(int *)p; if (s==sizeof(long)) return (unsigned long)*(long *)p; return 0; } /* _SL -- convert any integer type to unsigned-long WITH sign-extension */ long _SL( p, s ) void * p; size_t s; { if (s==sizeof(char)) return (char)*(char *)p; if (s==sizeof(short)) return (short)*(short *)p; if (s==sizeof(int)) return (int)*(int *)p; if (s==sizeof(long)) return (long)*(long *)p; return 0; } /**********************************************************************/ So, am I crazy and is this really dumb, or what? I don't know all the tricks to writing "portable" or "maintainable" code, so if someone could tell me how this is *supposed* to be done, I'd appreciate it. BTW, is what I've done here legal in ANSI? Mark Shepard shepard@cs.orst.edu || ...!hplabs!hp-pcd!orstcs!shepard