Skip to content

Instantly share code, notes, and snippets.

@n-b
Created April 15, 2012 18:45
Show Gist options
  • Save n-b/2394297 to your computer and use it in GitHub Desktop.
Save n-b/2394297 to your computer and use it in GitHub Desktop.
KVC compile-time checking macros
//
// PropertyMacros.h
//
//
// Created by Nicolas Bouilleaud on 12/04/12,
// using ideas by Uli Kusterer (http://orangejuiceliberationfront.com/safe-key-value-coding/)
// Laurent Deniau (https://groups.google.com/forum/?fromgroups#!topic/comp.std.c/d-6Mj5Lko_s)
// and Nick Forge (http://forgecode.net/2011/11/compile-time-checking-of-kvc-keys/)
//
//
// Usage :
// $keypath(foo) -> @"foo"
// $keypath(foo,bar) -> @"foo.bar"
// $keypath(foo,inexistentkey) -> compilation error: undeclared selector 'inexistentkey'
//
// Be sure to set -Wundeclared-selector.
//
#define PP_RSEQ_N() 9,8,7,6,5,4,3,2,1,0
#define PP_ARG_N(_1,_2,_3,_4,_5,_6,_7,_8,_9,N,...) N
#define PP_NARG_(...) PP_ARG_N(__VA_ARGS__)
#define PP_NARG(...) PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define KVCCHECK(p) NSStringFromSelector(@selector(p))
#define KVCCHECK_1(_1) KVCCHECK(_1)
#define KVCCHECK_2(_1,_2) KVCCHECK_1(_1),KVCCHECK_1(_2)
#define KVCCHECK_3(_1,_2,_3) KVCCHECK_1(_1),KVCCHECK_2(_2,_3)
#define KVCCHECK_4(_1,_2,_3,_4) KVCCHECK_1(_1),KVCCHECK_3(_2,_3,_4)
#define KVCCHECK_5(_1,_2,_3,_4,_5) KVCCHECK_1(_1),KVCCHECK_4(_2,_3,_4,_5)
#define KVCCHECK_6(_1,_2,_3,_4,_5,_6) KVCCHECK_1(_1),KVCCHECK_5(_2,_3,_4,_5,_6)
#define KVCCHECK_7(_1,_2,_3,_4,_5,_6,_7) KVCCHECK_1(_1),KVCCHECK_6(_2,_3,_4,_5,_6,_7)
#define KVCCHECK_8(_1,_2,_3,_4,_5,_6,_7,_8) KVCCHECK_1(_1),KVCCHECK_7(_2,_3,_4,_5,_6,_7,_8)
#define KVCCHECK_9(_1,_2,_3,_4,_5,_6,_7,_8,_9) KVCCHECK_1(_1),KVCCHECK_8(_2,_3,_4,_5,_6,_7,_8,_9)
#define KVCPATH_1(_1) @#_1
#define KVCPATH_2(_1,_2) @#_1"."#_2
#define KVCPATH_3(_1,_2,_3) @#_1"."#_2"."#_3
#define KVCPATH_4(_1,_2,_3,_4) @#_1"."#_2"."#_3"."#_4
#define KVCPATH_5(_1,_2,_3,_4,_5) @#_1"."#_2"."#_3"."#_4"."#_5
#define KVCPATH_6(_1,_2,_3,_4,_5,_6) @#_1"."#_2"."#_3"."#_4"."#_5"."#_6
#define KVCPATH_7(_1,_2,_3,_4,_5,_6,_7) @#_1"."#_2"."#_3"."#_4"."#_5"."#_6"."#_7
#define KVCPATH_8(_1,_2,_3,_4,_5,_6,_7,_8) @#_1"."#_2"."#_3"."#_4"."#_5"."#_6"."#_7"."#_8
#define KVCPATH_9(_1,_2,_3,_4,_5,_6,_7,_8,_9) @#_1"."#_2"."#_3"."#_4"."#_5"."#_6"."#_7"."#_8"."#_9
#define KP_CONCAT(a,b) a ## b
#define KP_XCONCAT(a,b) KP_CONCAT(a,b)
#define KP_(m, ...) m(__VA_ARGS__)
#define $keypath(...) (0?KP_(KP_XCONCAT(KVCCHECK_, PP_NARG(__VA_ARGS__)), __VA_ARGS__) :\
KP_(KP_XCONCAT(KVCPATH_, PP_NARG(__VA_ARGS__)), __VA_ARGS__) )
/*/../bin/ls > /dev/null
#This comment is a shell script to self-compile this .m file. Just run it.
if [ $# -eq 1 ]
then
echo compiling ${0%.*} with TEST_KEY_PATH=$1
clang $0 -DTEST_KEY_PATH="$1" -o ${0%.*}\
-framework Foundation\
-Wundeclared-selector -Werror\
-fno-show-source-location -fno-caret-diagnostics -fmacro-backtrace-limit=1 -fno-diagnostics-show-option \
&& ${0%.*} && rm ${0%.*}
exit
else
$0 a
$0 a,a
$0 a,a,a
$0 a,a,a,a
$0 a,a,a,a,a
$0 a,a,a,a,a,a
$0 a,a,a,a,a,a,a
$0 a,a,a,a,a,a,a,a
$0 a,a,a,a,a,a,a,a,a
$0 bam
$0 a,bam
$0 a,a,bam
$0 a,a,a,bam
$0 a,a,a,a,bam
$0 a,a,a,a,a,bam
$0 a,a,a,a,a,a,bam
$0 a,a,a,a,a,a,a,bam
$0 a,a,a,a,a,a,a,a,bam
fi
exit
*/
#import <Foundation/Foundation.h>
#import "PropertyMacros.h"
@interface A : NSObject
@end
@implementation A
- (A*) a { return [A new]; }
- (NSString*) description{ return @"yeah!"; }
@end
int main(int argc, char *argv[])
{
@autoreleasepool {
NSString * keypath;
keypath = $keypath(TEST_KEY_PATH);
NSLog(@"valueForKeypath:%@ = %@", keypath, [[A new] valueForKeyPath:keypath]);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment