Some of the most useful functions of Foundation that are not often well known are the functions to convert from non-objects to NSString:

NSStringFromRect(), NSStringFromRange(), NSStringFromClass(), etc…

For example, to print out a the bounds of a view:

NSLog(@"Bounds: %@", NSStringFromRect([view bounds]));

For some reason, I always have trouble remembering the names of these functions. Sometimes I think NSStringWithRect, sometimes I think NSRectToString, plus, I have to remember if it’s a rectangle, point, or size.

In order to simplify this and let my brain worry about more important details, I created a single macro to replace all these functions: DDToNSString:

NSLog(@"Bounds: %@", DDToNSString([view bounds]));

This works for NSPoint, NSSize, NSRect, NSRange, Class, SEL (selectors), and BOOL (using DDNSStringFromBOOL).

If you’re curious how it works, read on. If you just want to use it, it’s part of DDFoundation version 1.0b2 1.0b3.

If I wanted to require Objective-C++ I could just use an overloaded function. But I want this to work in plain Objective-C as well as Objective-C++. The only way to do this in essentially straight C is with a macro and some rather obscure GCC and Objective-C syntax. Here’s the declaration:

NSString * DDToStringFromTypeAndValue(const char * typeCode, void * value);

#define DDToNString(_X_) ({typeof(_X_) _Y_ = (_X_);\
    DDToStringFromTypeAndValue(@encode(typeof(_X_)), &_Y_);})

The macro is using Objective-C type encoding (the @encode keyword) to convert types into C strings. For example @encode(NSRect) returns:


The @encode operator takes a type as its argument. We can use the GCC typeof operator to get the type of an expression, hence the @encode(typeof(_X_)).

The function that does the actual work uses this encoded type string to call the correct Foundation function. Along with the type information, the macro passes a pointer to the value we want to convert. We have to use a pointer because we don’t know the type at compile to use pass by value. The void * will let us pass any pointer.

Finally, we want to be able to use this on return values, like our [view bounds] example:

NSLog(@"Bounds: %@", NSStringFromRect([view bounds]));

My original implementation of the macro was this simpler version:

#define DDToNString(_X_) \
    DDToStringFromTypeAndValue(@encode(typeof(_X_)), &(_X_))

This will not work with a return value because &(_X_) expands to &([view bounds]). You cannot take the address of a return value like that, only a variable. Thus, you’d have to put the result in a temporary variable:

NSRect bounds = [view bounds];
NSLog(@"Bounds: %@", NSStringFromRect(bounds));

The way to get around this problem is use another GCC extension allowing statements in expressions. Thus, the macro creates a temporary variable, _Y_, with the same type of _X_ (again using typeof) and passes the address of this temporary to the function. That’s how I ended up at:

#define DDToNString(_X_) ({typeof(_X_) _Y_ = (_X_);\
    DDToStringFromTypeAndValue(@encode(typeof(_X_)), &_Y_);})

The implementation of DDToStringFromTypeAndValue is pretty straightforward:

NSString * DDToStringFromTypeAndValue(const char * typeCode, void * value)
    if (strcmp(typeCode, @encode(NSPoint)) == 0)
        return NSStringFromPoint(*(NSPoint *)value);
    else if (strcmp(typeCode, @encode(NSSize)) == 0)
        return NSStringFromSize(*(NSSize *)value);
    // else if for other types...

This could probably be made more efficient by using bsearch(3) or a C++ map. But in the interested of keeping it simple and not optimizing until necessary, I think this is fine. I usually only use this feature for debug logging, anyways.