-
-
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]; | |
} |
Humbug. I think the reason people get annoyed with type safety is that they think it means a lot of extra work. And then they come out with solutions that produce the right result most of the time, but are by their very nature incorrect. Type-safety means that we won't be tricked into thinking things work.
But type-safety in a language lacking quality type inference IS A BITCH. Really.
It seems inference really is the key, yes, so that you can pass values around and store them in variables whose types are determined at compile time or even runtime. The programmer is not concerned with the types of the data, merely with the data itself and its manipulation; and it's the compiler's/runtime's job to check that the types are being properly combined.
That said, the need for autoboxing is an indication of a limited type system. For example, C++ doesn't need autoboxing because of templates.
(btw: please inform me if I have no idea what I'm talking about. Apart from it being late, I haven't been stretching myself programming-wise lately; just using Obj-C at work, and other technologies I'm familiar with.)
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)
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.
In short: Type safety is cool, but also sucks.