Skip to content

Instantly share code, notes, and snippets.

@jonsterling
Created August 20, 2010 06:37
Show Gist options
  • Save jonsterling/539747 to your computer and use it in GitHub Desktop.
Save jonsterling/539747 to your computer and use it in GitHub Desktop.
Objectify
#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>
#import <objc/message.h>
#import <string>
#import <map>
#import <typeinfo>
#define $(value) box<typeof(value)>(value)
using namespace std;
template <typename T> id box(T v) {
typedef pair<Class,SEL> m;
map<const char *,m> ops;
ops[@encode(int)] = m([NSNumber class], @selector(numberWithInt:));
ops[@encode(float)] = m([NSNumber class], @selector(numberWithFloat:));
ops[@encode(double)] = m([NSNumber class], @selector(numberWithDouble:));
ops[@encode(BOOL)] = m([NSNumber class], @selector(numberWithBool:));
ops[@encode(const char *)] = m([NSString class], @selector(stringWithUTF8String:));
ops[@encode(SEL)] = m([NSString class], @selector(objectify_stringFromSelector:));
ops[@encode(Class)] = m([NSString class], @selector(objectify_stringFromClass:));
ops[@encode(CGPoint)] = m([NSValue class], @selector(valueWithCGPoint:));
ops[@encode(CGRect)] = m([NSValue class], @selector(valueWithCGRect:));
ops[@encode(CGSize)] = m([NSValue class], @selector(valueWithCGSize:));
ops[@encode(NSRange)] = m([NSString class], @selector(objectify_stringFromRange:));
ops[@encode(void *)] = m([NSValue class], @selector(valueWithPointer:));
m method = ops[@encode(T)];
Class klass = method.first ? method.first : [NSObject class];
SEL s = method.second ? method.second : @selector(objectify_identity:);
id result = objc_msgSend(klass, s, v);
return result;
}
template <typename T> T unbox(id v) {
map<const char *,SEL> ops;
ops[@encode(int)] = @selector(intValue);
ops[@encode(float)] = @selector(floatValue);
ops[@encode(double)] = @selector(doubleValue);
ops[@encode(BOOL)] = @selector(boolValue);
ops[@encode(const char *)] = @selector(UTF8String);
ops[@encode(SEL)] = @selector(objectify_selectorValue);
ops[@encode(Class)] = @selector(objectify_classValue);
ops[@encode(CGPoint)] = @selector(CGPointValue);
ops[@encode(CGRect)] = @selector(CGRectValue);
ops[@encode(CGSize)] = @selector(CGSizeValue);
ops[@encode(NSRange)] = @selector(objectify_rangeValue);
ops[@encode(void *)] = @selector(pointerValue);
SEL method = ops[@encode(T)] ? ops[@encode(T)] : @selector(objectify_identity);
typedef T (*SendFunctionType)(id, SEL);
return ((SendFunctionType)objc_msgSend)(v, method);
}
#import "Objectify.h"
@implementation NSString (Objectify)
+ (id)objectify_stringFromSelector:(SEL)aSelector {
return NSStringFromSelector(aSelector);
}
- (SEL)objectify_selectorValue {
return NSSelectorFromString(self);
}
+ (id)objectify_stringFromClass:(Class)aClass {
return NSStringFromClass(aClass);
}
- (Class)objectify_classValue {
return NSClassFromString(self);
}
+ (id)objectify_stringFromRange:(NSRange)aRange{
return NSStringFromRange(aRange);
}
- (NSRange)objectify_rangeValue {
return NSRangeFromString(self);
}
@end
@implementation NSObject (Objectify)
+ (id)objectify_identity:(id)object { return object; }
- (id)objectify_identity { return self; }
@end
#import <Foundation/Foundation.h>
#import "Objectify.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
const char *str = "wut";
id o = $<const char *>(str);
NSLog(@"char: %s, string: %@, of class: %@", str, o, [o class]);
// => char: wut, string: wut, of class: NSCFString
[pool drain];
}
@jonsterling
Copy link
Author

True. When working with a good static type system, the way you reason about solutions changes; problems that were solved previously using metaprogramming can be solved just as succinctly elsewise. But metaprogramming is so damned fun…

@jonsterling
Copy link
Author

As for resource-intensive checking, just try compiling any Haskell program. Seems to work quickly enough for me… :)

@haikusw
Copy link

haikusw commented Aug 20, 2010

not really following along completely, but are you calling the objc version that gave warnings like so?

id to_object( void * x )
{
    return [NSNumber numberWithInt: (int)x];
}

int a = 42;
id foo = to_object( a );

???

If you do it this way it doesn't give a warning:

id to_object( void * x )
{
    return [NSNumber numberWithInt: *(int*)x];
}

int a = 42;
id foo = to_object( &a );

requires sending in the address of the thing you want converted though...

@jonsterling
Copy link
Author

Wow, Tyler, you're totally right. Should have thought of that. :)

@haikusw
Copy link

haikusw commented Aug 20, 2010

maybe it prevents detecting the type though... didn't try to actually do what you were trying to do so not sure. Another other option would be to use C's var-args as the parameter because you can get their types moderately easily. I'm almost motivated enough to have a non-C++ version to sit down and write it, but a little pressed for time at the moment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment