-
-
Save jonsterling/539747 to your computer and use it in GitHub Desktop.
#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]; | |
} |
I can certainly believe it's a performance measure, since a main reason to use primitives over boxed values is for performance. Much as it's not collectable and annoying, int
is faster than NSNumber
;)
Type checking at compile time seems like the best option, but in order to check, for example, a template enough to be sure it will work would involve traversing through the steps and types each place that template is used in the code. I would think that'd be a very resource-intensive operation. And it would also not allow such haxxery as truly dynamic typing — that is, if a type were read as input to the program and used (à la NSClassFromString
or Ruby's const_get
).
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…
As for resource-intensive checking, just try compiling any Haskell program. Seems to work quickly enough for me… :)
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...
Wow, Tyler, you're totally right. Should have thought of that. :)
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.
Yes, well said with respect to inference. As for autoboxing, I think you are right to a point. Haskell, at it's lowest implementation levels, does indeed involve autoboxing of primitives, as does Smalltalk; yet at the programmer's level, these are always values and objects respectively. From my extremely limited understanding, this sort of boxing is a performance measure, but I may be misunderstanding something.
(My Haskell bias demands I should point out the benefits of handling as much type-checking at compile-time as possible)