Typesafe typecasts
This is probably obvious to everyone else out there, but since I only just realized this, I thought others may be similarly dense, so I'd like to point out a little bit of Pascal ingenuity that can be applied to a common Mac programming problem.
There are many classes and structs on the Mac that exist in duplicates. For example, CoreGraphics has CGRect to represent Quartz coordinates, while AppKit uses NSRect. They're exactly the same in layout, but have separate names. Similarly, there are many toll-free bridged classes between CoreFoundation and Foundation. For example a CFURLRef can be used wherever an NSURL* is needed, as long as you typecast between the types so the compiler doesn't whine.
Now, typecasts are dangerous: Assume you have an NSString* storing a file path, and now you decide to change it into an NSURL*, because that's Apple's new recommended type for file references. You change it in the class header, and all call sites start giving you type errors, which you fix.
All call sites? Nope. In one spot you were typecasting your NSString* into a CFStringRef. Now you're typecasting an NSURL* to a CFStringRef, which is obviously wrong, but will only give you runtime errors. Ouch. So, what can you do? Well, there's a neat trick that Pascal programmers used to use to get around Pascal's refusal to typecast: They used a union. Unions are usually considered as type-unsafe, but in this case, they're just type-safe enough for our purposes:
CFStringRef UKNSToCFString( NSString* str ) { union ConversionUnion { NSString* mNSString; CFStringRef mCFString; } myUnion;myUnion.mNSString = str;
return myUnion.mCFString; }
The neat thing is that this function type-checks the input and output, but since it's a union, it lets us set an NSString* and get a CFStringRef. Our above change would cause a compiler warning now since we're passing an NSURL* into a function that takes an NSString*. It encodes the fact that an NSString and a CFString are the same, but only those.
Apple even does that. Look at NSRectFromCGRect() (I slightly cleaned up the code):
NSRect NSRectFromCGRect( CGRect cgrect ) { union ConversionUnion { NSRect ns; CGRect cg; }; return ((union ConversionUnion *)&cgrect)->ns; }
They're even a tad more efficient: Since the input parameter already is a CGRect, they just typecast it to a union ConversionUnion, saving you that one extra copy to an actual temporary local union variable. You could probably even get rid of the union completely and just typecast inside the function, as its input parameter type and return type already perfectly codify the conversion in a type-safe manner.
A neat trick for which I've created a header for a bunch of commonly bridged types and put it on my github.