C Switch for Non-Integers

In C, and thus Objective-C, a switch statement only works with integers, and the case labels all need to be constant. Strings seem like a natural thing to switch on, especially for command line interfaces. Unfortunately, they cannot be reduced to integer constants, so we cannot use them. Now that we have blocks in the language, we can create a dictionary where we associate any object, such as an NSString, with a block, and call the selected block after a simple dictionary lookup.

The syntax

//get a string from standard input
char str[256];
fgets(str, 256, stdin);
*(char *)memchr(str, '\n', 256) = '';
JPSwitch([NSString stringWithUTF8String:str]) {
JPCase(@"option one"):
NSLog(@"the first posibility was selected");

JPStringCase(Do Non-NSString objects work?):
NSLog(@"Yes, technically any object would work as a key,"
@"and anything else would work with an NSValue wrapper");
JPDefaultCase:
NSLog(@"None of the possibilities were selected. Oh noes!");
} JPSwitchEnd;
__JPSwitch([NSString stringWithUTF8String:str], ^{
}, @"option one", ^{ JPSwitchCase:
NSLog(@"the first posibility was selected");
}, @"Do Non-NSString objects work?", ^{ JPSwitchCase:
NSLog(@"Yes, technically any object would work as a key,"
@"and anything else would work with an NSValue wrapper");
}, &JPSwitchDefaultCaseIndicator, ^{ JPSwitchCase:
NSLog(@"None of the possiblities were selected. Oh noes!");
} , &JPSwitchTermationIndicator);
The result://JPSwitch.h
extern char JPSwitchTermationIndicator;
extern char JPSwitchDefaultCaseIndicator;
void __JPSwitch(id selector, ...);
#define JPSwitch(selector) __JPSwitch(selector, ^
#define JPCase(obj) }, obj, ^{ JPSwitchCase
#define JPStringCase(str) JPCase(@#str) //Preprocessor stringification
#define JPDefaultCase }, &JPSwitchDefaultCaseIndicator, ^{ JPSwitchCase
#define JPSwitchEnd , &JPSwitchTermationIndicator)
__JPSwitch
//JPSwitch.m
#import "JPSwitch.h"
char JPSwitchTermationIndicator, JPSwitchDefaultCaseIndicator;void __JPSwitch(id selector, ...) {
va_list args;
va_start(args, selector);
id caseLabel;
void(^caseBlock)();
void(^defaultCaseBlock)() = nil;
va_arg(args, void(^)()); //Eat empty first blockNSMapTable *allCases = [NSMapTable mapTableWithWeakToWeakObjects];
while ((caseLabel = va_arg(args, id)) != (void *)&JPSwitchTermationIndicator) {
caseBlock = va_arg(args, id);
if (caseLabel == (void *)&JPSwitchDefaultCaseIndicator) {
defaultCaseBlock = caseBlock;
} else if (caseLabel != nil) {
[allCases setObject:caseBlock forKey:caseLabel];
}
}
caseBlock = (void(^)())[allCases objectForKey:selector] ?: defaultCaseBlock;
if (caseBlock) caseBlock();
}

Conclusion

So now you can use a switch statement over any Objective-C object, including NSStrings. Additionally, you can switch with anything else by wrapping it in an NSValue. I think this could be best used in parsing and especially in command line interfaces.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store