Skip to content

Instantly share code, notes, and snippets.

@kylesluder
Created December 16, 2010 21:09
Show Gist options
  • Save kylesluder/744020 to your computer and use it in GitHub Desktop.
Save kylesluder/744020 to your computer and use it in GitHub Desktop.
An idea for using typeof on selector types
// It would be great if we had a typeof analogue that worked for selectors and evaluated to the type of the method that matches that selector.
@interface Foo : NSObject
- (NSString *)aMethod:(CGFloat)arg1 withArgs:(NSString *)arg2;
@end
void exampleOne() {
Foo *f = [Foo new];
NSString *s = (__typeof_msg__(f, @selector(aMethod:withArgs:))objc_msgSend)(f, 0, @"String");
NSNumber *n = (__typeof_msg__(f, @selector(aMethod:withArgs:))objc_msgSend)(f, 1.0, @"Hi"); // error: returns NSString*
NSString *r = (__typeof_msg__(f, @selector(aMethod:withArgs:))objc_msgSend)(f, @"Hello", @"World"); // error: expected CGFloat for arg 2
}
@interface Bar : NSObject
- (NSRect)aMethod:(id)arg1 withArgs:(char **)arg2;
@end
// The first argument is used to disambiguate between multiple methods with the same selector
void exampleThree() {
Foo *f = [Foo new];
Bar *b = [Bar new];
NSString *s = (__typeof_msg__(f, @selector(aMethod:withArgs:))objc_msgSend)(f, 3.14, @"Pi");
NSRect z = (__typeof_msg__(Bar, @selector(aMethod:withArgs:))objc_msgSend)(b, f, NULL); // also accepts class names, searches for instance methods that match the selector
}
// A variant would allow for sending class messages
@interface Bar (ClassMethod)
+ (double)aMethod:(char **)arg1 withArgs:(NSError **)arg2;
@end
void exampleThree() {
Bar *b = [Bar new];
char *s; NSError *e;
double l = (__typeof_msg__(b, @selector(aMethod:withArgs:))objc_msgSend)(b, &s, &e); // error: -[Bar aMethod:withArgs:] doesn't take char**,NSError** or return double
double qq = (__typeof_class_msg__(Bar, @selector(aMethod:withArgs:))objc_msgSend)(b, &s, &e);
double ll = (__typeof_class_msg__(b, @selector(aMethod:withArgs:))objc_msgSend)(b, &s, &e); // error: b is an instance, but __typeof_class_msg__ casts objc_msgSend to (double)(Class, char**, NSError**)
double xx = (__typeof_class_msg__(b, @selector(aMethod:withArgs:))objc_msgSend)([b class], &s, &e); // much better
}
// Could wrap it in a typesafe macro
#define TYPESAFE_MSGSEND(self, _cmd, ...) ((__typeof_msg__(self, _cmd))objc_msgSend)(self, _cmd, __VA_ARGS__))
#define TYPESAFE_CLASS_MSGSEND(self, _cmd, ...) ((__typeof_class_msg__(self, _cmd))objc_msgSend)(self, _cmd, __VA_ARGS__))
void exampleFour() {
Foo *f = [Foo new];
TYPESAFE_MSGSEND(f, @selector(aMethod:withArgs:), [NSString string], [NSString string]); // error: expected CGFloat for arg 2
char *c; NSError *e;
TYPESAFE_CLASS_MSGSEND(Bar, @selector(aMethod:withArgs:), &c, &e);
}
// And can store these function pointers as type-safe versions of objc_msgSend
void exampleFive(Foo *aFooInstance) {
static __typeof_msg__(Foo, @selector(aMethod:withArgs:)) foo_msgSend;
// note mismatch of first arg to __typeof_msg__. Above, it's the class name. Here, it's the instance. They mean the same thing
if (!foo_msgSend)
foo_msgSend = __typeof_msg__(f, @selector(aMethod:withArgs:)) dlsym(RTLD_DEFAULT, "fancy_optimized_msgSend");
foo_msgSend(aFooInstance, 2.7, @"Euler's number");
}
// You can use this to test for whether you should use objc_msgSend_fpret (and probably stret too, if I can figure out the syntax for a typeof expression for a function)
float floatReturn( /* accepts any args; this won't work in ObjC++ */ );
void exampleSix() {
Foo *f = [Foo new];
if (__builtin_types_compat_p(__typeof_msg__(Foo, @selector(aMethod:withArgs:)), typeof(floatReturn)))
float f = objc_msgSend_fpret(f, @selector(aMethod:withArgs:), 1.0, @"Float return");
else
id o = objc_msgSend(f, @selector(aMethod:withArgs:), 2.0, @"Object return");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment