Chris Chris - 4 months ago 16
iOS Question

C function macro to take NSString placeholders

In my IOS-Project, I have a custom Logger-class (singleton) containing the function

- (void)log:(NSString *)domain logLevel:(int)level logMessage:(NSString *)message


which is globally available through the following preprocessor macro:

#define MyLog(domain, level, message) [[MyLogger sharedInstance] log:domain logLevel:level logMessage:message]


Now when I make the call:

MyLog(@"common", LL_ERROR, @"There was an error!");


Everything works fine. But in practice, the logMessage will sometimes contain string placeholders. So the big question is:
How can I make my macro accept calls like

MyLog(@"common", LL_ERROR, @"There was an error: %@", [error debugDescription]);


With the current solution, Xcode complains: "Too many arguments provided to function-like macro invocation".

Answer

First, you have to change your log method to take a "variable argument list", for example like this:

- (void)log:(NSString *)domain logLevel:(int)level logMessage:(NSString *)message, ...
{
    va_list argList;
    va_start(argList, message);
    NSString *fullMessage = [[NSString alloc] initWithFormat:message arguments:argList];
    va_end(argList);

    NSLog(@"domain:%@, level:%d: %@", domain, level, fullMessage);
}

Then you have to change your MyLog macro to work with a variable number of arguments. This is a GNU feature (http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html), that works with Clang as well:

#define MyLog(domain, level, message, ...) \
   [[MyLogger sharedInstance] log:domain logLevel:level logMessage:message, ##__VA_ARGS__]

Now

MyLog(@"common", LL_ERROR, @"There was an error!");
MyLog(@"common", LL_ERROR, @"There was an error: %@", [error debugDescription]);

should both work without problems.