-
-
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]; | |
| } |
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.
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
NSClassFromStringor Ruby'sconst_get).