views:

56

answers:

3

I've written a macro in Objective-C to perform a safe cast. Here's what it looks like so far:

#define SAFE_CAST(OBJECT, TYPE) ([OBJECT isKindOfClass:[TYPE class]] ? (TYPE *) OBJECT: nil)

This works really well, but it'd be nice if there was a way to store OBJECT in a variable so it didn't get called twice. For instance, using the macro as such:

NSString *str = SAFE_CAST([dictinary objectForKey:key], NSString);

results in code similar to this when the macro is expanded:

NSString *str = ([[dictinary objectForKey:key] isKindOfClass:[NSString class]] ? (NSString *) [dictinary objectForKey:key]: nil);

I'd prefer for it to work more like this:

id obj = [dictionary objectForKey:key];
NSString *str = ([obj objectForKey:key] isKindOfClass[NSString class]] ? (NSString *) obj : nil);

Thanks.

A: 

So write it like that, just wrap it in do { } while(0) <-- and not just in parenthesis.

#define SAFE_CAST(OBJECT, TYPE, VAR) do { \
    id obj = OBJECT; \
    VAR = [obj isKindOfClass:[TYPE class]] ? (TYPE *) obj : nil; \
} while(0)
jer
I can't get this to compile correctly in the usage I used above. The compiler outputs: error: expected expression before 'do'.
helixed
Err you're right, sorry. First there's a syntax error after nil, missing ; and secondly, it needs to stand on its own. Meaning, either pass in another argument which assigns the output of the ternary operation, or do it a different way. :)
jer
No worries. Thanks for the reply anyway.
helixed
+3  A: 

You can use a GCC extension called statement statement expressions to have

#define SAFE_CAST(OBJECT, TYPE) ({ id obj=OBJECT;[obj isKindOfClass:[TYPE class]] ? (TYPE *) obj: nil; })

That said, I think it's generally a bad approach to have a situation where you need to use SAFE_CAST a lot. Never put objects of different classes in an array; never reuse an action message (IBAction)someAction:(id)sender for UI objects of different classes. Then you usually don't need to use SAFE_CAST.

Yuji
Right now I'm using this macro when reading information out of a plist. I'm not guaranteed the plist's contents are valid. It's like any other cast; it can be abused, but it has its uses. If there's a better way to do this, could you please elaborate? As per your reply, statement expressions look interesting. Can they be used with LLVM?
helixed
Yes it works with `clang`. As for `plist`, I agree with using `SAFE_CAST` if it's user-supplied. But if it's not, I think it's better to use `NSAssert` while developing, so that the program crashes hard when I prepare an invalid plist; the release build of your app shouldn't need `SAFE_CAST`, because you make sure `plist` is valid .
Yuji
Works great. Thanks.
helixed
+2  A: 

If you really think you must do this, you could use a function:

#define SAFE_CAST(Object, Type) (Type *)cast_helper(Object, [Type class])
static id cast_helper(id x, Class c) {
    return [x isKindOfClass:c] ? x : nil;
}
Georg Fritzsche
That's an interesting approach, but a macro just seemed a little cleaner. I'm trying to avoid having global functions floating around. Also, could you please elaborate on what you mean by 'If you really think you must do this'? Several other languages support safe casting, including C# and C++. Why shouldn't I use them?
helixed
@helixed: why do you think a global macro hanging around is any better than a global function hanging around?
JeremyP
@helixed: For the plist approach mentioned above it fits, its just that situation where those dynamic casts are really needed are rare. I don't see the specific problem with a global function though - i'd rather use functions than macros which do only straight-forward text-processing.
Georg Fritzsche
I see your point. Aside from some negligible overhead, this does seem like a better approach. Thanks.
helixed
@helixed: As long as the definition is visible at the point of the call, there will be no overhead - todays compilers inline aggressively wether a function is specified `inline` or not.
Georg Fritzsche