3

I have the following simplified code example:

// in .h file
// define a macro for partial method
#define debugWithMsg context:(CFStringRef)__FUNCTION__ lineNumber:__LINE__ debug:

@interface MyLogger : NSObject {
  ...
}

- (void) context:(CFStringRef)function 
      lineNumber:(int)line 
           debug:(NSString*)messageFormat, ...;
@end

I use this method in other classes to print debug messages to XCode console. Here is an example I test my debug method(in MyViewController class with a table view):

- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section {
  ...
  // myLogger is an instance of MyLogger.
  [myLogger debugWithMsg@"something for %@", @"testing!"];
  ...
}
...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  ...
  [myLogger debugWithMsg@"something for %@", @"another testing!"];
  ...
}

The above example works well; however, for me, it does not look like a standard C/C++ or ObjC syntax. The reason I need to use this macro is that I would like to pass the first two arguments as "constants" to the method. Actually, those two arguments are also macros: __FUNCTION__ being a C++ macro, and __LINE__ being a C standard macro. Those two special macros would be dynamically mapped to a string of the function and a line number where the method is called. For example the above debug method call prints a msg in XCode console like this:

[timestamp] -[MyViewController tableView:numberOfRowsInSection:] line:107 - something for testing!
...
[timestamp] -[MyViewController tableView:cellForRowAtIndexPath:] line:124 - something for another testing!

The MyLogger class is mainly for my internal use. Is there any other better practice to get the same result? Or is there any problem with this implementation?

I am thinking to define my macro in the same way as NSLog(fmt, ...), which is a macro as well:

MyDebugLog(instance, fmt, ....)

where instance is an instance of MyLogger, and fmt, ... are format string and var list. This is my trial definition of the macro:

#define MyDebugLog(logger, fmt, ...) \
  [logger, context:(CFStringRef)__FUNCTION__ lineNumber:__LINE__ \
   debug:fmt, ## _VA_ARGS__]

I got compiling error saying "'context' undeclared" in my code where the macro is used:

- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section {
  ...
  // myLogger is an instance of MyLogger.
  MyDebugLog(myLogger, @"something for %@", @"testing!"); // compiling error!
  ...
}

Not sure what's wrong in my definition. Any suggestions?

David.Chu.ca
  • 37,408
  • 63
  • 148
  • 190

1 Answers1

7

This is what I use.

#define SFLog(message, ...) NSLog((@"SFLOG: %s [Line %d] " message), __PRETTY_FUNCTION__, __LINE__, ## __VA_ARGS__)

You use it exactly like SFLog(@"Some var %f", myFloat); except it prepends the 'SFLog' (I also use 'SFError') and the __PRETTY_FUNCTION__ macros.


[EDIT]

I found the SO post where I got this. It's over here.


[Edit]

Commenter Jonathan Leffler points out below that the macro can be defined without the ##. The ## is purely a GCC thing, and doesn't conform to C99 standard. ## also works with LLVM/Clang 1.5 (XCode 3.2.4) as well as compiler settings for -std=c99 and -std=gnu99. Just understand that there are some limitations for the macro if it doesn't work as expected.

Community
  • 1
  • 1
Stephen Furlani
  • 6,794
  • 4
  • 31
  • 60
  • @Jonathan, two hashes works for me. You may only need one, I admit to not knowing enough to tell you the difference. – Stephen Furlani Dec 22 '10 at 14:58
  • Odd...it "shouldn't" work; the single hash is the stringization operator; the double hash is the token pasting operator. Actually, thinking about it, the double hash simply shouldn't be there, but that doesn't explain why it is being allowed. – Jonathan Leffler Dec 22 '10 at 15:00
  • @Jonathan, according to the SO post where I got it, the `##` prevents possible macro expansion of the arguments. – Stephen Furlani Dec 22 '10 at 15:02
  • Just checked - it works both ways (as in, GCC's C Preprocessor does not complain. And for simple cases, produces the same result. Let me play with macros for a bit... – Jonathan Leffler Dec 22 '10 at 15:04
  • @Jonathan, interestingly enough, the same question was posted to the original author of the macro. It may be a compiler trick/hack, which is why it's not common. – Stephen Furlani Dec 22 '10 at 15:06
  • I see no evidence of `##` suppressing macro expansion. It seems to be 'harmless', even if a little unorthodox. – Jonathan Leffler Dec 22 '10 at 15:08
  • The other post needs fixing...the GCC manual says: GNU CPP permits ... omit the variable arguments ... the compiler would complain, ... the expansion of the macro ... has the extra comma after the format string. ... CPP behaves specially for ... ‘`##`’. If instead you write `#define debug(format, ...) fprintf (stderr, format, ## __VA_ARGS__)` and if the variable arguments are omitted or empty, the ‘`##`’ operator causes the preprocessor to remove the comma before it. ... GNU CPP does not complain about the paste operation ... (GCC 4.4.1 manual §5.17) – Jonathan Leffler Dec 22 '10 at 15:16
  • Here's a version of SFLog that works with no variable arguments and without the GCC-specific token pasting operator: `#define SFLog3(message, ...) NSLog((@"SFLOG: %s [Line %d] " message), __PRETTY_FUNCTION__, __LINE__ __VA_ARGS__)`. However, the C99 standard says (§6.10.3/4) "Otherwise, there shall be more arguments in the invocation than there are parameters in the macro definition (excluding the ...)." in a context where 'otherwise' means the macro has ', ...' at the end. It appears that allowing zero arguments for the ellipsis is a GNU extension. – Jonathan Leffler Dec 22 '10 at 15:27
  • Of course, quoting the C99 standard for Objective-C is a tad dangerous, but I'm not sure whether there is any other Objective-C compiler than GCC. – Jonathan Leffler Dec 22 '10 at 15:30
  • @Jonathan, the macro also works with LLVM/Clang compiler 1.5, so I'm not sure a strict C99 macro will be of use to the OP, although it is interesting to know. I work primarily in Obj-C++. – Stephen Furlani Dec 22 '10 at 15:36
  • @Jonathan, your `SFLog3` function doesn't compile for me. I get lots of "expected ')' before 'myvar'" for both Clang and GCC. I'm compiling Obj-C++. – Stephen Furlani Dec 22 '10 at 15:40
  • Re: SFLog3 - Interesting...Oh, OK - yes...it only works with zero arguments; I missed the missing comma with arguments. Guilty of not compiling (only running cpp and inspecting the output)... Now; how do we clean up this mess of comments. Do you want to copy some of the information up into your answer where it can be more easily read. I can get you the complete quote from the GCC manual, if you want it, and/or more information from C99. – Jonathan Leffler Dec 22 '10 at 15:53
  • hahaha, it might be better to post your own answer, being more knowledgeable about the topic. I just answered out of my experience, hoping the macro could help the OP. – Stephen Furlani Dec 22 '10 at 15:57
  • Looks like the one I want. I am going to update my macro and test it out. Thanks! – David.Chu.ca Dec 22 '10 at 17:24
  • I tried to update my macro, but I could not figure it out. See my updated question. – David.Chu.ca Dec 22 '10 at 19:56