Skip to content
This repository has been archived by the owner on Dec 5, 2019. It is now read-only.

Commit

Permalink
Merge branch 'EXTPassthrough'
Browse files Browse the repository at this point in the history
  • Loading branch information
jspahrsummers committed Jul 3, 2012
2 parents 10e41f7 + fada0e6 commit 8256f34
Show file tree
Hide file tree
Showing 6 changed files with 301 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -19,6 +19,7 @@ libextobjc currently includes the following features:
* **Algebraic data types** generated completely at compile-time, defined using EXTADT.
* EXTBlockTarget, which extends the target-action mechanism with support for blocks.
* EXTTuple, for multiple return values and assignment.
* EXTPassthrough, to automatically implement methods that simply invoke the same method on another object.
* Better variadic arguments, with support for packaging the arguments up as an array, using EXTVarargs.
* Aspect-oriented programming, using EXTAspect.
* Block-based coroutines, using EXTCoroutine.
Expand Down
14 changes: 14 additions & 0 deletions Tests/EXTPassthroughTest.h
@@ -0,0 +1,14 @@
//
// EXTPassthroughTest.h
// extobjc
//
// Created by Justin Spahr-Summers on 2012-07-03.
// Released into the public domain.
//

#import <SenTestingKit/SenTestingKit.h>
#import "EXTPassthrough.h"

@interface EXTPassthroughTest : SenTestCase

@end
86 changes: 86 additions & 0 deletions Tests/EXTPassthroughTest.m
@@ -0,0 +1,86 @@
//
// EXTPassthroughTest.m
// extobjc
//
// Created by Justin Spahr-Summers on 2012-07-03.
// Released into the public domain.
//

#import "EXTPassthroughTest.h"

@interface InnerClass : NSObject
@property (nonatomic, getter = isEnabled) BOOL enabled;

- (void)voidMethod;
- (int)methodWithString:(NSString *)str;
- (int)methodWithString:(NSString *)str number:(NSNumber *)num;
@end

@interface OuterClass : NSObject
@property (nonatomic, strong) InnerClass *inner;
@end

@interface OuterClass (DelegatedMethods)
@property (nonatomic, getter = isEnabled) BOOL enabled;

- (void)renamedMethod;
- (int)methodWithString:(NSString *)str;
- (int)methodWithString:(NSString *)str number:(NSNumber *)num;
@end

@implementation EXTPassthroughTest

- (void)testPassthroughMethods {
OuterClass *outer = [[OuterClass alloc] init];
STAssertNotNil(outer, @"");

[outer renamedMethod];
STAssertEquals([outer methodWithString:@"foo"], 3, @"");
STAssertEquals([outer methodWithString:@"foobar" number:@5], 11, @"");
}

- (void)testPassthroughProperty {
OuterClass *outer = [[OuterClass alloc] init];
STAssertNotNil(outer, @"");
STAssertFalse(outer.enabled, @"");
STAssertFalse(outer.inner.enabled, @"");

outer.enabled = YES;
STAssertTrue(outer.enabled, @"");
STAssertTrue(outer.inner.enabled, @"");
}

@end

@implementation OuterClass
@passthrough(OuterClass, renamedMethod, self.inner, voidMethod);
@passthrough(OuterClass, methodWithString:, self.inner);
@passthrough(OuterClass, methodWithString:number:, [self inner]);
@passthrough(OuterClass, isEnabled, self.inner);
@passthrough(OuterClass, setEnabled:, self.inner);

- (id)init {
self = [super init];
if (!self)
return nil;

self.inner = [[InnerClass alloc] init];
return self;
}

@end

@implementation InnerClass

- (void)voidMethod {
}

- (int)methodWithString:(NSString *)str {
return [self methodWithString:str number:nil];
}

- (int)methodWithString:(NSString *)str number:(NSNumber *)num {
return (int)[str length] + [num intValue];
}

@end
14 changes: 14 additions & 0 deletions extobjc.xcodeproj/project.pbxproj
Expand Up @@ -135,6 +135,10 @@
D0CD1BE8158F2A2A0039C845 /* EXTTupleTest.m in Sources */ = {isa = PBXBuildFile; fileRef = D0CD1BE6158F2A2A0039C845 /* EXTTupleTest.m */; };
D0CFB61F128A8F12006DC377 /* EXTConcreteProtocolTest.m in Sources */ = {isa = PBXBuildFile; fileRef = D0C1D917128A57B600E96C0F /* EXTConcreteProtocolTest.m */; };
D0CFB620128A8F12006DC377 /* EXTSwizzleTest.m in Sources */ = {isa = PBXBuildFile; fileRef = D0A8B30D128A4B56004AACE0 /* EXTSwizzleTest.m */; };
D0D1F92E15A396CC002E2387 /* EXTPassthrough.h in Headers */ = {isa = PBXBuildFile; fileRef = D0D1F92C15A396CC002E2387 /* EXTPassthrough.h */; };
D0D1F92F15A396CC002E2387 /* EXTPassthrough.h in Headers */ = {isa = PBXBuildFile; fileRef = D0D1F92C15A396CC002E2387 /* EXTPassthrough.h */; };
D0D1F93415A396F7002E2387 /* EXTPassthroughTest.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D1F93315A396F7002E2387 /* EXTPassthroughTest.m */; };
D0D1F93515A396F7002E2387 /* EXTPassthroughTest.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D1F93315A396F7002E2387 /* EXTPassthroughTest.m */; };
D0E6A0EE159BB43B00FB92FC /* EXTAnnotation.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E6A0EC159BB43B00FB92FC /* EXTAnnotation.h */; };
D0E6A0EF159BB43B00FB92FC /* EXTAnnotation.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E6A0EC159BB43B00FB92FC /* EXTAnnotation.h */; };
D0E6A0F0159BB43B00FB92FC /* EXTAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E6A0ED159BB43B00FB92FC /* EXTAnnotation.m */; };
Expand Down Expand Up @@ -304,6 +308,9 @@
D0CD1BE6158F2A2A0039C845 /* EXTTupleTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EXTTupleTest.m; sourceTree = "<group>"; };
D0CFB60F128A8EEE006DC377 /* iOS Tests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "iOS Tests.octest"; sourceTree = BUILT_PRODUCTS_DIR; };
D0CFB610128A8EEE006DC377 /* iOS-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "iOS-Info.plist"; sourceTree = "<group>"; };
D0D1F92C15A396CC002E2387 /* EXTPassthrough.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTPassthrough.h; sourceTree = "<group>"; };
D0D1F93215A396F7002E2387 /* EXTPassthroughTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTPassthroughTest.h; sourceTree = "<group>"; };
D0D1F93315A396F7002E2387 /* EXTPassthroughTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EXTPassthroughTest.m; sourceTree = "<group>"; };
D0E6A0EC159BB43B00FB92FC /* EXTAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTAnnotation.h; sourceTree = "<group>"; };
D0E6A0ED159BB43B00FB92FC /* EXTAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EXTAnnotation.m; sourceTree = "<group>"; };
D0E6A0F2159BB46D00FB92FC /* EXTAnnotationTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTAnnotationTest.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -435,6 +442,7 @@
D005F02B15950509007A8A1C /* EXTMultiObject.m */,
D005F02C15950509007A8A1C /* EXTNil.h */,
D005F02D15950509007A8A1C /* EXTNil.m */,
D0D1F92C15A396CC002E2387 /* EXTPassthrough.h */,
D005F03015950509007A8A1C /* EXTPrivateMethod.h */,
D005F03115950509007A8A1C /* EXTPrivateMethod.m */,
D005F03215950509007A8A1C /* EXTProtocolCategory.h */,
Expand Down Expand Up @@ -591,6 +599,8 @@
D0FE94031596668400F3AE1C /* EXTMultimethodTest.m */,
D002DAF613656CDF005348A5 /* EXTNilTest.h */,
D002DAF713656CDF005348A5 /* EXTNilTest.m */,
D0D1F93215A396F7002E2387 /* EXTPassthroughTest.h */,
D0D1F93315A396F7002E2387 /* EXTPassthroughTest.m */,
D033765B131E53DB0039ACFD /* EXTPrivateMethodTest.h */,
D033765C131E53DB0039ACFD /* EXTPrivateMethodTest.m */,
D0E7E907128F8DD200FE0263 /* EXTProtocolCategoryTest.h */,
Expand Down Expand Up @@ -651,6 +661,7 @@
D0FE93FF1596665D00F3AE1C /* EXTMultimethod.h in Headers */,
D09FB2F7159A41C400A5F6A4 /* EXTSelectorChecking.h in Headers */,
D0E6A0EF159BB43B00FB92FC /* EXTAnnotation.h in Headers */,
D0D1F92F15A396CC002E2387 /* EXTPassthrough.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -687,6 +698,7 @@
D0FE93FE1596665D00F3AE1C /* EXTMultimethod.h in Headers */,
D09FB2F6159A41C400A5F6A4 /* EXTSelectorChecking.h in Headers */,
D0E6A0EE159BB43B00FB92FC /* EXTAnnotation.h in Headers */,
D0D1F92E15A396CC002E2387 /* EXTPassthrough.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -923,6 +935,7 @@
D0FE94041596668400F3AE1C /* EXTMultimethodTest.m in Sources */,
D09FB2FA159A41D100A5F6A4 /* EXTSelectorCheckingTest.m in Sources */,
D0E6A0F4159BB46D00FB92FC /* EXTAnnotationTest.m in Sources */,
D0D1F93415A396F7002E2387 /* EXTPassthroughTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -951,6 +964,7 @@
D0FE94051596668400F3AE1C /* EXTMultimethodTest.m in Sources */,
D09FB2FB159A41D100A5F6A4 /* EXTSelectorCheckingTest.m in Sources */,
D0E6A0F5159BB46D00FB92FC /* EXTAnnotationTest.m in Sources */,
D0D1F93515A396F7002E2387 /* EXTPassthroughTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
185 changes: 185 additions & 0 deletions extobjc/EXTPassthrough.h
@@ -0,0 +1,185 @@
//
// EXTPassthrough.h
// extobjc
//
// Created by Justin Spahr-Summers on 2012-07-03.
// Released into the public domain.
//

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "metamacros.h"

/**
* \@passthrough defines \a METHOD on \a CLASS to simply invoke a method on
* another object and return the result. The object to message should be an
* expression passed as the third argument to the macro, and may refer to \c
* self (for instance, to access a property).
*
* By default, the message sent to the other object uses the same method name
* given to the macro. \a METHOD may pass through to a method by a different
* name by passing a fourth argument to the macro, which should be the name of
* the message to send.
*
* @code
//
// OuterClass.h
//
@interface OuterClass : NSObject
@end
@interface OuterClass (PassthroughMethods)
@property (nonatomic, getter = isEnabled) BOOL enabled;
- (void)renamedMethod;
- (int)methodWithString:(NSString *)str;
@end
//
// OuterClass.m
//
@interface InnerClass : NSObject
@property (nonatomic, getter = isEnabled) BOOL enabled;
- (void)voidMethod;
- (int)methodWithString:(NSString *)str;
@end
@interface OuterClass ()
@property (nonatomic, strong) InnerClass *inner;
@end
@implementation OuterClass
@passthrough(OuterClass, renamedMethod, self.inner, voidMethod);
@passthrough(OuterClass, methodWithString:, self.inner);
@passthrough(OuterClass, isEnabled, self.inner);
@passthrough(OuterClass, setEnabled:, self.inner);
- (id)init {
self = [super init];
if (!self)
return nil;
self.inner = [InnerClass new];
return self;
}
@end
@implementation InnerClass
...
@end
* @endcode
*
* @note \a METHOD must denote an instance method.
*
* @note To avoid "incomplete implementation" warnings, passthrough methods and
* properties may be declared in a category on \a CLASS, as opposed to the main
* \@interface block.
*/
#define passthrough(CLASS, METHOD, ...) \
class CLASS; \
\
passthrough_(__COUNTER__, CLASS, METHOD, __VA_ARGS__)

/*** implementation details follow ***/ \
#define passthrough_(ID, CLASS, METHOD, ...) \
static id \
(*metamacro_concat(ext_originalMethodSignatureForSelector_, ID)) \
(id, SEL, SEL); \
\
static NSMethodSignature * \
metamacro_concat(ext_methodSignatureForSelector_, ID) \
(CLASS *self, SEL _cmd, SEL selector) { \
if (selector != @selector(METHOD)) \
return metamacro_concat(ext_originalMethodSignatureForSelector_, ID)(self, _cmd, selector); \
\
id inner = metamacro_head(__VA_ARGS__); \
SEL innerSelector = passthrough_renamed_method(METHOD, __VA_ARGS__); \
return [inner methodSignatureForSelector:innerSelector]; \
} \
\
static BOOL \
(*metamacro_concat(ext_originalRespondsToSelector_, ID)) \
(id, SEL, SEL); \
\
static BOOL \
metamacro_concat(ext_respondsToSelector_, ID) \
(CLASS *self, SEL _cmd, SEL selector) { \
if (selector != @selector(METHOD)) \
return metamacro_concat(ext_originalRespondsToSelector_, ID)(self, _cmd, selector); \
\
id inner = metamacro_head(__VA_ARGS__); \
SEL innerSelector = passthrough_renamed_method(METHOD, __VA_ARGS__); \
return [inner respondsToSelector:innerSelector]; \
} \
\
static void \
(*metamacro_concat(ext_originalForwardInvocation_, ID)) \
(id, SEL, id); \
\
static void \
metamacro_concat(ext_forwardInvocation_, ID) \
(CLASS *self, SEL _cmd, NSInvocation *invocation) { \
if (invocation.selector != @selector(METHOD)) { \
metamacro_concat(ext_originalForwardInvocation_, ID)(self, _cmd, invocation); \
return; \
} \
\
[invocation setTarget:metamacro_head(__VA_ARGS__)]; \
[invocation setSelector:passthrough_renamed_method(METHOD, __VA_ARGS__)]; \
[invocation invoke]; \
} \
\
__attribute__((constructor)) \
static void metamacro_concat(ext_passthrough_injection_, ID) (void) { \
Class outerClass = objc_getClass(# CLASS); \
\
Method methodSignatureForSelector = class_getInstanceMethod(outerClass, @selector(methodSignatureForSelector:)); \
Method respondsToSelector = class_getInstanceMethod(outerClass, @selector(respondsToSelector:)); \
Method forwardInvocation = class_getInstanceMethod(outerClass, @selector(forwardInvocation:)); \
\
metamacro_concat(ext_originalMethodSignatureForSelector_, ID) = \
(id (*)(id, SEL, SEL))method_getImplementation(methodSignatureForSelector); \
\
metamacro_concat(ext_originalRespondsToSelector_, ID) = \
(BOOL (*)(id, SEL, SEL))method_getImplementation(respondsToSelector); \
\
metamacro_concat(ext_originalForwardInvocation_, ID) = \
(void (*)(id, SEL, id))method_getImplementation(forwardInvocation); \
\
class_replaceMethod( \
outerClass, \
@selector(methodSignatureForSelector:), \
(IMP)&metamacro_concat(ext_methodSignatureForSelector_, ID), \
method_getTypeEncoding(methodSignatureForSelector) \
); \
\
class_replaceMethod( \
outerClass, \
@selector(respondsToSelector:), \
(IMP)&metamacro_concat(ext_respondsToSelector_, ID), \
method_getTypeEncoding(respondsToSelector) \
); \
\
class_replaceMethod( \
outerClass, \
@selector(forwardInvocation:), \
(IMP)&metamacro_concat(ext_forwardInvocation_, ID), \
method_getTypeEncoding(forwardInvocation) \
); \
}

#define passthrough_renamed_method(METHOD, ...) \
@selector(metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \
( \
/* no renaming */ \
METHOD \
) \
( \
/* the renamed method follows the passthrough target */ \
metamacro_at(1, __VA_ARGS__) \
) \
)
1 change: 1 addition & 0 deletions extobjc/extobjc.h
Expand Up @@ -20,6 +20,7 @@
#import "EXTMultimethod.h"
#import "EXTMultiObject.h"
#import "EXTNil.h"
#import "EXTPassthrough.h"
#import "EXTPrivateMethod.h"
#import "EXTProtocolCategory.h"
#import "EXTSafeCategory.h"
Expand Down

0 comments on commit 8256f34

Please sign in to comment.