fp-obj-c
//#!/usr/bin/env objc-run | |
#import <Foundation/Foundation.h> | |
#import <objc/runtime.h> | |
#import <objc/message.h> | |
/// | |
/// Typedefs | |
typedef id (^TargetMethod)(id arg); | |
typedef id (^InstanceProperty)(id target); | |
typedef InstanceProperty (^InstanceMethod)(id arg); | |
#define DefineMethod(method, argType, returnType)\ | |
- (returnType (^)(argType)) method;\ | |
+ (returnType (^(^)(id target))(argType)) method; | |
#define DefineProperty(method, returnType)\ | |
+ (returnType (^)(id target)) method; | |
/// | |
/// Dispatch | |
@implementation NSObject (FuncsDefinitions) | |
+ (BOOL) pfunc_resolveInstanceMethod:(SEL)sel | |
{ | |
NSString * pfuncName = NSStringFromSelector(sel); | |
// The methods we’re adding have no arguments in the real Objective-C sense. | |
if(![pfuncName hasSuffix:@":"]) { | |
SEL methodSelector = NSSelectorFromString([pfuncName stringByAppendingString:@":"]); | |
NSMethodSignature * signature = [self instanceMethodSignatureForSelector:methodSelector]; | |
// We’re looking for an existing method of the same name, with 1 argument. | |
// (We’d need to check the argument and return types.) | |
if(signature) { | |
// We’ve found our original method. Let’s add ours: | |
// First add a method that returns a TargetMethod block. | |
IMP imp_i = imp_implementationWithBlock(^(id target){ | |
return (TargetMethod)^(id arg) { | |
return ((id(*)(id,SEL,id))objc_msgSend)(target, methodSelector, arg); | |
}; | |
}); | |
class_addMethod(self, sel, imp_i, "@?@:"); | |
// And add a class method (that returns an InstanceMethod block, that returns an InstanceProperty) | |
IMP imp_c = imp_implementationWithBlock(^(id targetClass){ | |
return (InstanceMethod)^(id arg) { | |
return (InstanceProperty)^(id target) { | |
NSAssert(self==targetClass, @"Adding a class method: the receiver should be the class."); | |
return ((id(*)(id,SEL,id))objc_msgSend)(target, methodSelector, arg); | |
}; | |
}; | |
}); | |
// `object_getClass` returns our metaclass. Our class methods are our metaclass’s instance methods. | |
class_addMethod(object_getClass(self), sel, imp_c, "@?@:"); | |
return YES; | |
} | |
} | |
// Call the original implementation | |
return [self pfunc_resolveInstanceMethod:sel]; | |
} | |
+ (BOOL) pfunc_resolveClassMethod:(SEL)sel | |
{ | |
NSString * pfuncName = NSStringFromSelector(sel); | |
// The methods we’re adding have no arguments in the real Objective-C sense. | |
if(![pfuncName hasSuffix:@":"]) { | |
NSMethodSignature * signature = [self instanceMethodSignatureForSelector:sel]; | |
// We’re looking for an existing method of the same name, with 0 argument. | |
// (We’d need to check the return type.) | |
if(signature) { | |
// Add a method that returns a InstanceProperty block. | |
IMP imp_i = imp_implementationWithBlock(^(id targetClass){ | |
return (InstanceProperty)^(id target) { | |
return ((id(*)(id,SEL))objc_msgSend)(target, sel); | |
}; | |
}); | |
class_addMethod(object_getClass(self), sel, imp_i, "@?@:"); | |
return YES; | |
} | |
} | |
// Call the original implementation | |
return [self pfunc_resolveInstanceMethod:sel]; | |
} | |
+ (void)load { | |
method_exchangeImplementations(class_getClassMethod(self, @selector(pfunc_resolveInstanceMethod:)), | |
class_getClassMethod(self, @selector(resolveInstanceMethod:))); | |
method_exchangeImplementations(class_getClassMethod(self, @selector(pfunc_resolveClassMethod:)), | |
class_getClassMethod(self, @selector(resolveClassMethod:))); | |
} | |
@end | |
/// | |
/// Test code | |
void Print(id<NSObject> obj) { | |
printf("%s\n",obj.description.UTF8String); | |
} | |
@implementation NSArray (Map_) | |
- (NSArray*) map:(id (^)(id target))block_ | |
{ | |
NSMutableArray * result = [NSMutableArray new]; | |
for (id target in self) { | |
[result addObject:block_(target)]; | |
} | |
return result; | |
} | |
- (NSArray*) reduce:(id (^(^)(id arg))(id target))block_ | |
{ | |
id result = [self firstObject]; | |
if([self count]<2) { | |
return result; | |
} | |
for (id target in [self subarrayWithRange:NSMakeRange(1, [self count]-1)]) { | |
result = block_(target)(result); | |
} | |
return result; | |
} | |
@end | |
@implementation NSString (Plus) | |
- (NSString*) plus:(NSString*) string | |
{ | |
return [self stringByAppendingString:string]; | |
} | |
@end | |
/// | |
/// | |
@interface NSArray (Funcs) | |
DefineMethod(map, id (^)(id), NSArray*); | |
DefineMethod(reduce, id (^(^)(id))(id), NSArray*); | |
@end | |
@interface NSString (Funcs) | |
DefineProperty(uppercaseString, NSString*); | |
DefineMethod(plus, NSString*, NSString*); | |
@end | |
/// | |
/// | |
int main() | |
{ | |
Print(@[@"foo", @"bar", @"baz"].map(NSString.uppercaseString)); | |
// -> ( FOO, BAR, BAZ ) | |
Print(@"Hello".plus(@" I love you")); | |
// -> Hello I love you | |
Print(@[@"foo", @"bar", @"baz"].reduce(NSString.plus)); | |
// -> foobarbaz | |
InstanceProperty massUppercaser = NSArray.map(NSString.uppercaseString); | |
Print(massUppercaser(@[@"abc",@"def"])); | |
// -> ( ABC, DEF ) | |
InstanceProperty exclamate = NSString.plus(@"!!"); | |
Print(@[@"quick", @" to the batmobile"].map(NSString.uppercaseString).map(exclamate).reduce(NSString.plus)); | |
// -> QUICK!! TO THE BATMOBILE!! | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment