├── .gitignore ├── README.md ├── testtuples ├── testtuples.xcodeproj │ └── project.pbxproj └── testtuples │ ├── main.m │ ├── testtuples-Prefix.pch │ └── testtuples.1 ├── tuples.h └── tuples.m /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | tuples 2 | ====== 3 | 4 | Objective-C tuples -------------------------------------------------------------------------------- /testtuples/testtuples.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 75368C94168CADEC00B8F5EC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 75368C93168CADEC00B8F5EC /* Foundation.framework */; }; 11 | 75368C97168CADEC00B8F5EC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 75368C96168CADEC00B8F5EC /* main.m */; }; 12 | 75368C9B168CADEC00B8F5EC /* testtuples.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 75368C9A168CADEC00B8F5EC /* testtuples.1 */; }; 13 | 75368CA2168CAEE100B8F5EC /* tuples.m in Sources */ = {isa = PBXBuildFile; fileRef = 75368CA1168CAEE100B8F5EC /* tuples.m */; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXCopyFilesBuildPhase section */ 17 | 75368C8D168CADEC00B8F5EC /* CopyFiles */ = { 18 | isa = PBXCopyFilesBuildPhase; 19 | buildActionMask = 2147483647; 20 | dstPath = /usr/share/man/man1/; 21 | dstSubfolderSpec = 0; 22 | files = ( 23 | 75368C9B168CADEC00B8F5EC /* testtuples.1 in CopyFiles */, 24 | ); 25 | runOnlyForDeploymentPostprocessing = 1; 26 | }; 27 | /* End PBXCopyFilesBuildPhase section */ 28 | 29 | /* Begin PBXFileReference section */ 30 | 75368C8F168CADEC00B8F5EC /* testtuples */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testtuples; sourceTree = BUILT_PRODUCTS_DIR; }; 31 | 75368C93168CADEC00B8F5EC /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 32 | 75368C96168CADEC00B8F5EC /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 33 | 75368C99168CADEC00B8F5EC /* testtuples-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "testtuples-Prefix.pch"; sourceTree = ""; }; 34 | 75368C9A168CADEC00B8F5EC /* testtuples.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = testtuples.1; sourceTree = ""; }; 35 | 75368CA1168CAEE100B8F5EC /* tuples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = tuples.m; path = ../../tuples.m; sourceTree = ""; }; 36 | /* End PBXFileReference section */ 37 | 38 | /* Begin PBXFrameworksBuildPhase section */ 39 | 75368C8C168CADEC00B8F5EC /* Frameworks */ = { 40 | isa = PBXFrameworksBuildPhase; 41 | buildActionMask = 2147483647; 42 | files = ( 43 | 75368C94168CADEC00B8F5EC /* Foundation.framework in Frameworks */, 44 | ); 45 | runOnlyForDeploymentPostprocessing = 0; 46 | }; 47 | /* End PBXFrameworksBuildPhase section */ 48 | 49 | /* Begin PBXGroup section */ 50 | 75368C84168CADEC00B8F5EC = { 51 | isa = PBXGroup; 52 | children = ( 53 | 75368C95168CADEC00B8F5EC /* testtuples */, 54 | 75368C92168CADEC00B8F5EC /* Frameworks */, 55 | 75368C90168CADEC00B8F5EC /* Products */, 56 | ); 57 | sourceTree = ""; 58 | }; 59 | 75368C90168CADEC00B8F5EC /* Products */ = { 60 | isa = PBXGroup; 61 | children = ( 62 | 75368C8F168CADEC00B8F5EC /* testtuples */, 63 | ); 64 | name = Products; 65 | sourceTree = ""; 66 | }; 67 | 75368C92168CADEC00B8F5EC /* Frameworks */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | 75368C93168CADEC00B8F5EC /* Foundation.framework */, 71 | ); 72 | name = Frameworks; 73 | sourceTree = ""; 74 | }; 75 | 75368C95168CADEC00B8F5EC /* testtuples */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | 75368C96168CADEC00B8F5EC /* main.m */, 79 | 75368CA1168CAEE100B8F5EC /* tuples.m */, 80 | 75368C9A168CADEC00B8F5EC /* testtuples.1 */, 81 | 75368C98168CADEC00B8F5EC /* Supporting Files */, 82 | ); 83 | path = testtuples; 84 | sourceTree = ""; 85 | }; 86 | 75368C98168CADEC00B8F5EC /* Supporting Files */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 75368C99168CADEC00B8F5EC /* testtuples-Prefix.pch */, 90 | ); 91 | name = "Supporting Files"; 92 | sourceTree = ""; 93 | }; 94 | /* End PBXGroup section */ 95 | 96 | /* Begin PBXNativeTarget section */ 97 | 75368C8E168CADEC00B8F5EC /* testtuples */ = { 98 | isa = PBXNativeTarget; 99 | buildConfigurationList = 75368C9E168CADEC00B8F5EC /* Build configuration list for PBXNativeTarget "testtuples" */; 100 | buildPhases = ( 101 | 75368C8B168CADEC00B8F5EC /* Sources */, 102 | 75368C8C168CADEC00B8F5EC /* Frameworks */, 103 | 75368C8D168CADEC00B8F5EC /* CopyFiles */, 104 | ); 105 | buildRules = ( 106 | ); 107 | dependencies = ( 108 | ); 109 | name = testtuples; 110 | productName = testtuples; 111 | productReference = 75368C8F168CADEC00B8F5EC /* testtuples */; 112 | productType = "com.apple.product-type.tool"; 113 | }; 114 | /* End PBXNativeTarget section */ 115 | 116 | /* Begin PBXProject section */ 117 | 75368C86168CADEC00B8F5EC /* Project object */ = { 118 | isa = PBXProject; 119 | attributes = { 120 | LastUpgradeCheck = 0450; 121 | ORGANIZATIONNAME = Chocolat; 122 | }; 123 | buildConfigurationList = 75368C89168CADEC00B8F5EC /* Build configuration list for PBXProject "testtuples" */; 124 | compatibilityVersion = "Xcode 3.2"; 125 | developmentRegion = English; 126 | hasScannedForEncodings = 0; 127 | knownRegions = ( 128 | en, 129 | ); 130 | mainGroup = 75368C84168CADEC00B8F5EC; 131 | productRefGroup = 75368C90168CADEC00B8F5EC /* Products */; 132 | projectDirPath = ""; 133 | projectRoot = ""; 134 | targets = ( 135 | 75368C8E168CADEC00B8F5EC /* testtuples */, 136 | ); 137 | }; 138 | /* End PBXProject section */ 139 | 140 | /* Begin PBXSourcesBuildPhase section */ 141 | 75368C8B168CADEC00B8F5EC /* Sources */ = { 142 | isa = PBXSourcesBuildPhase; 143 | buildActionMask = 2147483647; 144 | files = ( 145 | 75368C97168CADEC00B8F5EC /* main.m in Sources */, 146 | 75368CA2168CAEE100B8F5EC /* tuples.m in Sources */, 147 | ); 148 | runOnlyForDeploymentPostprocessing = 0; 149 | }; 150 | /* End PBXSourcesBuildPhase section */ 151 | 152 | /* Begin XCBuildConfiguration section */ 153 | 75368C9C168CADEC00B8F5EC /* Debug */ = { 154 | isa = XCBuildConfiguration; 155 | buildSettings = { 156 | ALWAYS_SEARCH_USER_PATHS = NO; 157 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 158 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 159 | CLANG_CXX_LIBRARY = "libc++"; 160 | CLANG_ENABLE_OBJC_ARC = YES; 161 | CLANG_WARN_EMPTY_BODY = YES; 162 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 163 | COPY_PHASE_STRIP = NO; 164 | GCC_C_LANGUAGE_STANDARD = gnu99; 165 | GCC_DYNAMIC_NO_PIC = NO; 166 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 167 | GCC_OPTIMIZATION_LEVEL = 0; 168 | GCC_PREPROCESSOR_DEFINITIONS = ( 169 | "DEBUG=1", 170 | "$(inherited)", 171 | ); 172 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 173 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 174 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 175 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 176 | GCC_WARN_UNUSED_VARIABLE = YES; 177 | MACOSX_DEPLOYMENT_TARGET = 10.8; 178 | ONLY_ACTIVE_ARCH = YES; 179 | SDKROOT = macosx; 180 | }; 181 | name = Debug; 182 | }; 183 | 75368C9D168CADEC00B8F5EC /* Release */ = { 184 | isa = XCBuildConfiguration; 185 | buildSettings = { 186 | ALWAYS_SEARCH_USER_PATHS = NO; 187 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 188 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 189 | CLANG_CXX_LIBRARY = "libc++"; 190 | CLANG_ENABLE_OBJC_ARC = YES; 191 | CLANG_WARN_EMPTY_BODY = YES; 192 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 193 | COPY_PHASE_STRIP = YES; 194 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 195 | GCC_C_LANGUAGE_STANDARD = gnu99; 196 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 197 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 198 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 199 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 200 | GCC_WARN_UNUSED_VARIABLE = YES; 201 | MACOSX_DEPLOYMENT_TARGET = 10.8; 202 | SDKROOT = macosx; 203 | }; 204 | name = Release; 205 | }; 206 | 75368C9F168CADEC00B8F5EC /* Debug */ = { 207 | isa = XCBuildConfiguration; 208 | buildSettings = { 209 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 210 | GCC_PREFIX_HEADER = "testtuples/testtuples-Prefix.pch"; 211 | PRODUCT_NAME = "$(TARGET_NAME)"; 212 | }; 213 | name = Debug; 214 | }; 215 | 75368CA0168CADEC00B8F5EC /* Release */ = { 216 | isa = XCBuildConfiguration; 217 | buildSettings = { 218 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 219 | GCC_PREFIX_HEADER = "testtuples/testtuples-Prefix.pch"; 220 | PRODUCT_NAME = "$(TARGET_NAME)"; 221 | }; 222 | name = Release; 223 | }; 224 | /* End XCBuildConfiguration section */ 225 | 226 | /* Begin XCConfigurationList section */ 227 | 75368C89168CADEC00B8F5EC /* Build configuration list for PBXProject "testtuples" */ = { 228 | isa = XCConfigurationList; 229 | buildConfigurations = ( 230 | 75368C9C168CADEC00B8F5EC /* Debug */, 231 | 75368C9D168CADEC00B8F5EC /* Release */, 232 | ); 233 | defaultConfigurationIsVisible = 0; 234 | defaultConfigurationName = Release; 235 | }; 236 | 75368C9E168CADEC00B8F5EC /* Build configuration list for PBXNativeTarget "testtuples" */ = { 237 | isa = XCConfigurationList; 238 | buildConfigurations = ( 239 | 75368C9F168CADEC00B8F5EC /* Debug */, 240 | 75368CA0168CADEC00B8F5EC /* Release */, 241 | ); 242 | defaultConfigurationIsVisible = 0; 243 | }; 244 | /* End XCConfigurationList section */ 245 | }; 246 | rootObject = 75368C86168CADEC00B8F5EC /* Project object */; 247 | } 248 | -------------------------------------------------------------------------------- /testtuples/testtuples/main.m: -------------------------------------------------------------------------------- 1 | // Created by Alex Gordon on 27/12/2012. 2 | #import 3 | #import "../../tuples.h" 4 | 5 | BOOL eq(id a, id b) { 6 | if (a == b) return YES; 7 | if ([a isEqual:b]) return YES; 8 | return NO; 9 | } 10 | 11 | int cunt(id val) { 12 | if ([val respondsToSelector:@selector(count)]) return (int)[val count]; 13 | if ([val respondsToSelector:@selector(length)]) return (int)[val length]; 14 | return 0; 15 | } 16 | 17 | #define ass(x) assert(x) 18 | #define asseq(a, b) assert(eq(a, b)) 19 | 20 | int main(int argc, const char * argv[]) { 21 | @autoreleasepool { 22 | 23 | Tuple* tup = tuple(nil, @10, @"Hello", @[ @"a", @"b" ], nil); 24 | 25 | NSArray* arr = @[ @"a", @"b" ]; 26 | asseq(tup[-1], nil); 27 | asseq(tup[0], nil); 28 | asseq(tup[1], @10); 29 | asseq(tup[2], @"Hello"); 30 | asseq(tup[3], arr); 31 | asseq(tup[4], nil); 32 | asseq(tup[5], nil); 33 | 34 | Tuple* tup2 = [tup map:^id(id x) { 35 | return @(cunt(x)); 36 | }]; 37 | 38 | asseq(tup2[-1], nil); 39 | asseq(tup2[0], @0); 40 | asseq(tup2[1], @0); 41 | asseq(tup2[2], @5); 42 | asseq(tup2[3], @2); 43 | asseq(tup2[4], @0); 44 | asseq(tup2[5], nil); 45 | 46 | Tuple* tup3 = [[Tuple alloc] initWithArray:arr]; 47 | asseq(tup3[-1], nil); 48 | asseq(tup3[0], @"a"); 49 | asseq(tup3[1], @"b"); 50 | asseq(tup3[2], nil); 51 | 52 | asseq([tup3 first], @"a"); 53 | asseq([tup3 second], @"b"); 54 | asseq([tup3 third], nil); 55 | 56 | id a, b; unpack(tup3, &a, &b); 57 | asseq(a, @"a"); 58 | asseq(b, @"b"); 59 | 60 | id comparedto = @"a"; 61 | for (id obj in tup3) { 62 | asseq(obj, comparedto); 63 | comparedto = @"b"; 64 | } 65 | 66 | NSLog(@"All good"); 67 | } 68 | return 0; 69 | } 70 | 71 | -------------------------------------------------------------------------------- /testtuples/testtuples/testtuples-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'testtuples' target in the 'testtuples' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /testtuples/testtuples/testtuples.1: -------------------------------------------------------------------------------- 1 | .\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. 2 | .\"See Also: 3 | .\"man mdoc.samples for a complete listing of options 4 | .\"man mdoc for the short list of editing options 5 | .\"/usr/share/misc/mdoc.template 6 | .Dd 27/12/2012 \" DATE 7 | .Dt testtuples 1 \" Program name and manual section number 8 | .Os Darwin 9 | .Sh NAME \" Section Header - required - don't modify 10 | .Nm testtuples, 11 | .\" The following lines are read in generating the apropos(man -k) database. Use only key 12 | .\" words here as the database is built based on the words here and in the .ND line. 13 | .Nm Other_name_for_same_program(), 14 | .Nm Yet another name for the same program. 15 | .\" Use .Nm macro to designate other names for the documented program. 16 | .Nd This line parsed for whatis database. 17 | .Sh SYNOPSIS \" Section Header - required - don't modify 18 | .Nm 19 | .Op Fl abcd \" [-abcd] 20 | .Op Fl a Ar path \" [-a path] 21 | .Op Ar file \" [file] 22 | .Op Ar \" [file ...] 23 | .Ar arg0 \" Underlined argument - use .Ar anywhere to underline 24 | arg2 ... \" Arguments 25 | .Sh DESCRIPTION \" Section Header - required - don't modify 26 | Use the .Nm macro to refer to your program throughout the man page like such: 27 | .Nm 28 | Underlining is accomplished with the .Ar macro like this: 29 | .Ar underlined text . 30 | .Pp \" Inserts a space 31 | A list of items with descriptions: 32 | .Bl -tag -width -indent \" Begins a tagged list 33 | .It item a \" Each item preceded by .It macro 34 | Description of item a 35 | .It item b 36 | Description of item b 37 | .El \" Ends the list 38 | .Pp 39 | A list of flags and their descriptions: 40 | .Bl -tag -width -indent \" Differs from above in tag removed 41 | .It Fl a \"-a flag as a list item 42 | Description of -a flag 43 | .It Fl b 44 | Description of -b flag 45 | .El \" Ends the list 46 | .Pp 47 | .\" .Sh ENVIRONMENT \" May not be needed 48 | .\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 49 | .\" .It Ev ENV_VAR_1 50 | .\" Description of ENV_VAR_1 51 | .\" .It Ev ENV_VAR_2 52 | .\" Description of ENV_VAR_2 53 | .\" .El 54 | .Sh FILES \" File used or created by the topic of the man page 55 | .Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact 56 | .It Pa /usr/share/file_name 57 | FILE_1 description 58 | .It Pa /Users/joeuser/Library/really_long_file_name 59 | FILE_2 description 60 | .El \" Ends the list 61 | .\" .Sh DIAGNOSTICS \" May not be needed 62 | .\" .Bl -diag 63 | .\" .It Diagnostic Tag 64 | .\" Diagnostic informtion here. 65 | .\" .It Diagnostic Tag 66 | .\" Diagnostic informtion here. 67 | .\" .El 68 | .Sh SEE ALSO 69 | .\" List links in ascending order by section, alphabetically within a section. 70 | .\" Please do not reference files that do not exist without filing a bug report 71 | .Xr a 1 , 72 | .Xr b 1 , 73 | .Xr c 1 , 74 | .Xr a 2 , 75 | .Xr b 2 , 76 | .Xr a 3 , 77 | .Xr b 3 78 | .\" .Sh BUGS \" Document known, unremedied bugs 79 | .\" .Sh HISTORY \" Document history if command behaves in a unique manner -------------------------------------------------------------------------------- /tuples.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | id tupleSentinel(); 4 | 5 | @interface Tuple : NSObject 6 | 7 | // Creation 8 | - (id)init; // Empty tuple 9 | - (id)initWithArray:(NSArray*)arr; 10 | - (id)initWithObjects:(id)objects, ...; 11 | 12 | // Getting an object at an index 13 | - (id)objectAtIndexedSubscript:(NSUInteger)idx; 14 | - (id)objectAtIndex:(int)idx; 15 | 16 | - (id)first; 17 | - (id)second; 18 | - (id)third; 19 | 20 | // Unpacking 21 | - (void)unpack:(id*)pointypointers, ...; 22 | 23 | // Algorithms 24 | - (Tuple*)map:(id (^)(id x))mapping; 25 | 26 | @end 27 | 28 | #ifndef tuple 29 | #define tuple(...) [[Tuple alloc] initWithObjects:__VA_ARGS__, tupleSentinel()] 30 | #else 31 | #warning Can't load fileability/tuples.m, because tuple is already defined 32 | #endif 33 | 34 | #ifndef unpack 35 | #define unpack(tup, ...) [tup unpack:__VA_ARGS__, NULL] 36 | #endif 37 | 38 | #if 0 39 | // Usage: 40 | Tuple* tup = tuple(nil, @10, @"abc") 41 | t[0] --> nil 42 | [t second] --> @10 43 | t[2] --> @"abc" 44 | t[3] --> nil // By design, it is impossible to know the length of a tuple, since you should already know... Any access outside the bounds of the tuple turns into a nil 45 | 46 | id a, b; unpack(tup, &a, &b); --> a = nil, b = @10 47 | #endif 48 | 49 | -------------------------------------------------------------------------------- /tuples.m: -------------------------------------------------------------------------------- 1 | #import "tuples.h" 2 | 3 | #if __has_feature(objc_arc) 4 | #define arc_unsafe_unretained __unsafe_unretained 5 | #define arc_autoreleasing __autoreleasing 6 | #define arc_bridge(t, x) ((__bridge t)x) 7 | #else 8 | #define arc_unsafe_unretained 9 | #define arc_autoreleasing 10 | #define arc_bridge(t, x) (x) 11 | #endif 12 | 13 | 14 | id tupleSentinel() { 15 | static id sentin; 16 | static dispatch_once_t onceToken; 17 | dispatch_once(&onceToken, ^{ 18 | sentin = [[NSObject alloc] init]; 19 | }); 20 | return sentin; 21 | } 22 | 23 | @interface Tuple () 24 | 25 | - (NSPointerArray*)_storage; 26 | - (void)_setStorage:(NSPointerArray*)newstorage; 27 | 28 | @end 29 | 30 | @implementation Tuple { 31 | NSPointerArray* storage; 32 | } 33 | - (NSPointerArray*)_storage { 34 | return storage; 35 | } 36 | - (void)_setStorage:(NSPointerArray*)newstorage { 37 | storage = newstorage; 38 | } 39 | 40 | 41 | // Initialization 42 | - (id)init { 43 | self = [super init]; 44 | if (!self) 45 | return nil; 46 | 47 | storage = [NSPointerArray pointerArrayWithOptions:NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPersonality]; 48 | 49 | return self; 50 | } 51 | - (id)initWithArray:(NSArray*)arr { 52 | self = [self init]; 53 | if (!self) 54 | return nil; 55 | 56 | for (id obj in arr) { 57 | [storage addPointer:arc_bridge(void*, obj)]; 58 | } 59 | 60 | return self; 61 | } 62 | - (id)initWithObjects:(id)objects, ... { 63 | self = [self init]; 64 | if (!self) 65 | return nil; 66 | 67 | va_list ap; 68 | va_start(ap, objects); 69 | 70 | id obj = objects; 71 | id sentin = tupleSentinel(); 72 | while (obj != sentin) { 73 | [storage addPointer:arc_bridge(void*, obj)]; 74 | obj = va_arg(ap, id); 75 | } 76 | va_end(ap); 77 | 78 | return self; 79 | } 80 | 81 | // Protocolic Obligations 82 | - (id)copyWithZone:(NSZone *)zone { 83 | id newtup = [[[self class] alloc] init]; 84 | [newtup _setStorage:[[self _storage] copy]]; 85 | return newtup; 86 | } 87 | - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id arc_unsafe_unretained [])stackbuf count:(NSUInteger)len { 88 | return [storage countByEnumeratingWithState:state objects:stackbuf count:len]; 89 | } 90 | 91 | // Getting an object at an index 92 | - (id)objectAtIndexedSubscript:(NSUInteger)idx { 93 | return [self objectAtIndex:(int)idx]; 94 | } 95 | - (id)objectAtIndex:(int)idx { 96 | if (idx < 0 || idx >= (int)[storage count]) 97 | return nil; 98 | return (__strong id)[storage pointerAtIndex:idx]; 99 | } 100 | 101 | - (id)first { 102 | return [self objectAtIndex:0]; 103 | } 104 | - (id)second { 105 | return [self objectAtIndex:1]; 106 | } 107 | - (id)third { 108 | return [self objectAtIndex:2]; 109 | } 110 | 111 | // Unpacking 112 | - (void)unpack:(id*)pointypointers, ... { 113 | 114 | va_list ap; 115 | va_start(ap, pointypointers); 116 | 117 | arc_autoreleasing id* pp = pointypointers; 118 | int i = 0; 119 | while (pp != NULL) { 120 | *pp = [self objectAtIndex:i]; 121 | 122 | pp = va_arg(ap, arc_autoreleasing id*); 123 | i++; 124 | } 125 | va_end(ap); 126 | 127 | } 128 | 129 | // Algorithms 130 | - (Tuple*)map:(id (^)(id x))mapping { 131 | 132 | Tuple* newtup = [self copy]; 133 | NSPointerArray* newstorage = [newtup _storage]; 134 | for (int i = 0, n = (int)[newstorage count]; i != n; i++) { 135 | id old = (__strong id)[storage pointerAtIndex:i]; 136 | [newstorage replacePointerAtIndex:i withPointer:arc_bridge(void*, mapping(old))]; 137 | } 138 | 139 | return newtup; 140 | } 141 | 142 | @end 143 | --------------------------------------------------------------------------------