Last active
December 25, 2015 16:19
-
-
Save boredzo/7005221 to your computer and use it in GitHub Desktop.
Macro to implement compile-time checking of KVC keys and key paths, but only in debug builds.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
/*Usage: | |
*PRH_KVCKey(employer) | |
*PRH_KVCKeyPath(employer, employees, count) | |
*/ | |
#define NDEBUG 1 | |
//A selector that cannot be a valid KVC key, but is a known selector (i.e., the compiler won't warn that it's unknown), and is long enough that its length is probably unique. | |
#if TARGET_OS_IPHONE | |
# define PRH_RidiculouslyLongButNonethelessValidSelector migrateStoreFromURL:type:options:withMappingModel:toDestinationURL:destinationType:destinationOptions:error: | |
# define PRH_RidiculouslyLongButNonethelessValidSelectorLength 108 | |
#else | |
# define PRH_RidiculouslyLongButNonethelessValidSelector initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bitmapFormat:bytesPerRow:bitsPerPixel: | |
# define PRH_RidiculouslyLongButNonethelessValidSelectorLength 148 | |
#endif | |
//Two code paths: One using string literal concatenation; one using componentsJoinedByString:. | |
//Probably should be investigated: Whether all these length messages completely wash away the speed benefit of string literal concatenation. | |
//PRH_KVCKeyPath is defined unconditionally below; the macros it uses internally are defined conditionally. | |
#if NDEBUG | |
# define PRH_KVCKey(k) @#k | |
//We'll concatenate all of the key path segments, whether they're valid KVC keys or not, into one giant string. | |
# define PRH_KVCKeyPathForRealsies3(num, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z) \ | |
(a @"." b @"." c @"." d @"." e @"." f @"." g @"." h @"." i @"." j @"." k @"." l @"." m @"." n @"." o @"." p @"." q @"." r @"." s @"." t @"." u @"." v @"." w @"." x @"." y @"." z) | |
//Next, we cut off the string after all of the valid KVC keys and the periods between them, using substringToIndex:, passing the total length of all of the valid KVC keys and the total number of periods. | |
//We pass an argument's length if it isn't the length of the Ridiculously Long Selector (on the assumption that no valid KVC key will have that exact length); otherwise, we pass zero. | |
//We count a period if and only if the arguments on either side are both valid KVC keys (again, by comparing length to that of the Ridiculously Long Selector). | |
# define PRH_KVCKeyPathForRealsies2(num, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z) \ | |
[PRH_KVCKeyPathForRealsies3(num, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z) substringToIndex:( \ | |
(([a length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [a length] : 0) \ | |
+ (([a length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([b length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([b length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [b length] : 0) \ | |
+ (([b length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([c length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([c length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [c length] : 0) \ | |
+ (([c length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([d length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([d length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [d length] : 0) \ | |
+ (([d length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([e length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([e length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [e length] : 0) \ | |
+ (([e length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([f length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([f length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [f length] : 0) \ | |
+ (([f length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([g length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([g length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [g length] : 0) \ | |
+ (([g length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([h length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([h length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [h length] : 0) \ | |
+ (([h length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([i length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([i length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [i length] : 0) \ | |
+ (([i length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([j length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([j length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [j length] : 0) \ | |
+ (([j length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([k length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([k length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [k length] : 0) \ | |
+ (([k length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([l length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([l length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [l length] : 0) \ | |
+ (([l length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([m length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([m length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [m length] : 0) \ | |
+ (([m length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([n length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([n length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [n length] : 0) \ | |
+ (([n length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([o length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([o length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [o length] : 0) \ | |
+ (([o length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([p length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([p length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [p length] : 0) \ | |
+ (([p length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([q length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([q length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [q length] : 0) \ | |
+ (([q length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([r length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([r length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [r length] : 0) \ | |
+ (([r length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([s length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([s length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [s length] : 0) \ | |
+ (([s length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([t length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([t length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [t length] : 0) \ | |
+ (([t length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([u length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([u length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [u length] : 0) \ | |
+ (([u length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([v length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([v length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [v length] : 0) \ | |
+ (([v length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([w length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([w length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [w length] : 0) \ | |
+ (([w length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([x length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([x length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [x length] : 0) \ | |
+ (([x length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([y length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([y length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [y length] : 0) \ | |
+ (([y length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) && ([z length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength)) \ | |
\ | |
+ (([z length] != PRH_RidiculouslyLongButNonethelessValidSelectorLength) ? [z length] : 0) \ | |
)] | |
#else | |
//This is where the compile-time checking magic happens. With -Wundeclared-selector, a KVC key that refers to a property that has never been declared will cause a warning when the key shows up in this @selector literal. | |
# define PRH_KVCKey(k) NSStringFromSelector(@selector(k)) | |
# define PRH_KVCKeyPathForRealsies3(num, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z) \ | |
[[@[a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z] subarrayWithRange:(NSRange){ .location = 0, .length = num }] componentsJoinedByString:@"."] | |
//This variant of PRH_KVCKeyPathForRealsies2 is just a pass-through. We only have a PRH_KVCKeyPathForRealsies3 in this branch for symmetry. | |
# define PRH_KVCKeyPathForRealsies2(num, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z) PRH_KVCKeyPathForRealsies3(num, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z) | |
#endif | |
//PRH_NumberOfArguments adapted from https://gist.github.com/kongtomorrow/6803313 | |
//Each argument passed to PRH_NumberOfArguments shifts one number off the end into PRH_NumberOfArguments1's ... bucket. | |
//Whichever one lands in num is the number of arguments passed to PRH_NumberOfArguments. | |
//Example: PRH_NumberOfArguments(foo, bar, baz) = PRH_NumberOfArguments1(foo, bar, baz, 26 …, 3, ...) | |
#define PRH_NumberOfArguments(...) PRH_NumberOfArguments1(__VA_ARGS__, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) | |
#define PRH_NumberOfArguments1(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, num, ...) num | |
//This step wraps all of the arguments in PRH_KVCKey. | |
#define PRH_KVCKeyPathForRealsies1(num, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, ...) PRH_KVCKeyPathForRealsies2(num, PRH_KVCKey(a), PRH_KVCKey(b), PRH_KVCKey(c), PRH_KVCKey(d), PRH_KVCKey(e), PRH_KVCKey(f), PRH_KVCKey(g), PRH_KVCKey(h), PRH_KVCKey(i), PRH_KVCKey(j), PRH_KVCKey(k), PRH_KVCKey(l), PRH_KVCKey(m), PRH_KVCKey(n), PRH_KVCKey(o), PRH_KVCKey(p), PRH_KVCKey(q), PRH_KVCKey(r), PRH_KVCKey(s), PRH_KVCKey(t), PRH_KVCKey(u), PRH_KVCKey(v), PRH_KVCKey(w), PRH_KVCKey(x), PRH_KVCKey(y), PRH_KVCKey(z)) | |
//First, we pass along the number of key path segments actually passed in; next, all of the segments; finally, enough copies of the Ridiculously Long Selector to pad out an empty argument list. | |
#define PRH_KVCKeyPath(...) PRH_KVCKeyPathForRealsies1(PRH_NumberOfArguments(__VA_ARGS__), __VA_ARGS__, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector, PRH_RidiculouslyLongButNonethelessValidSelector) | |
@interface Foo: NSObject | |
@property (copy) NSString *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k, *l, *m, *n, *o, *p, *q, *r, *s, *t, *u, *v, *w, *x, *y, *z; | |
@end | |
int main(int argc, char *argv[]) { | |
@autoreleasepool { | |
//These first two lines will emit warnings when -Wundeclared-selector is on. | |
NSLog(@"%@", PRH_KVCKeyPath(fred, barney)); | |
NSLog(@"%@", PRH_KVCKeyPath(foo, bar, baz)); | |
//This line won't emit a warning because these properties were declared above. | |
NSLog(@"%@", PRH_KVCKeyPath(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z)); | |
} | |
} | |
@implementation Foo | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment