aboutsummaryrefslogtreecommitdiff
path: root/DebugUtils/GTMMethodCheck.h
blob: 7a6fe9fda7c7f8fe0252974ce029bd25d5e02e5b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//
//  GTMMethodCheck.h
//  
//  Copyright 2006-2008 Google Inc.
//
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
//  use this file except in compliance with the License.  You may obtain a copy
//  of the License at
// 
//  http://www.apache.org/licenses/LICENSE-2.0
// 
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
//  License for the specific language governing permissions and limitations under
//  the License.
//

#import <Foundation/Foundation.h>
#import <stdio.h>
#import <sysexits.h>

/// A macro for enforcing debug time checks to make sure all required methods are linked in
// 
// When using categories, it can be very easy to forget to include the
// implementation of a category. 
// Let's say you had a class foo that depended on method bar of class baz, and
// method bar was implemented as a member of a category.
// You could add the following code:
// @implementation foo
// GTM_METHOD_CHECK(baz, bar)
// @end
// and the code would check to make sure baz was implemented just before main
// was called. This works for both dynamic libraries, and executables.
//
// Classes (or one of their superclasses) being checked must conform to the 
// NSObject protocol. We will check this, and spit out a warning if a class does 
// not conform to NSObject.
//
// This is not compiled into release builds.

#ifdef DEBUG

#ifdef __cplusplus
extern "C" {
#endif
  
// If you get an error for GTMMethodCheckMethodChecker not being defined, 
// you need to link in GTMMethodCheck.m. We keep it hidden so that we can have 
// it living in several separate images without conflict.
// Functions with the ((constructor)) attribute are called after all +loads
// have been called. See "Initializing Objective-C Classes" in 
// http://developer.apple.com/documentation/DeveloperTools/Conceptual/DynamicLibraries/Articles/DynamicLibraryDesignGuidelines.html#//apple_ref/doc/uid/TP40002013-DontLinkElementID_20
  
__attribute__ ((constructor, visibility("hidden"))) void GTMMethodCheckMethodChecker(void);
  
#ifdef __cplusplus
};
#endif

// This is the "magic".
// A) we need a multi layer define here so that the stupid preprocessor
//    expands __LINE__ out the way we want it. We need LINE so that each of
//    out GTM_METHOD_CHECKs generates a unique class method for the class.
#define GTM_METHOD_CHECK(class, method) GTM_METHOD_CHECK_INNER(class, method, __LINE__)
#define GTM_METHOD_CHECK_INNER(class, method, line) GTM_METHOD_CHECK_INNER_INNER(class, method, line)

// B) Create up a class method called xxGMethodCheckMethod+class+line that the 
//    GTMMethodCheckMethodChecker function can look for and call. We
//    look for GTMMethodCheckMethodChecker to enforce linkage of
//    GTMMethodCheck.m.
#define GTM_METHOD_CHECK_INNER_INNER(class, method, line) \
+ (void)xxGMMethodCheckMethod ## class ## line { \
  void (*addr)() = GTMMethodCheckMethodChecker; \
  if (addr && ![class instancesRespondToSelector:@selector(method)] \
      && ![class respondsToSelector:@selector(method)]) { \
    fprintf(stderr, "%s:%d: error: We need method '%s' to be linked in for class '%s'\n", \
            __FILE__, line, #method, #class); \
    exit(EX_SOFTWARE); \
  } \
}

#else // !DEBUG

// Do nothing in release.
#define GTM_METHOD_CHECK(class, method)

#endif  // DEBUG