Skip to content

Instantly share code, notes, and snippets.

@boredzo
Last active December 25, 2015 16:19
Show Gist options
  • Save boredzo/7005221 to your computer and use it in GitHub Desktop.
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.
#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