tags:

views:

55

answers:

2

I can't get my head around the syntax for multiple arguments in Objective-C. I have seen this question, but the answer hasn't helped me (yet).

Here is my code (actually I will want to eventually pass to NSString stringWithFormat, but getting an NSLog to work would be good enough for now):

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
 // Insert code here to initialize your application 
 [self log:@"blah blah %d", 32];
}


- (void)log:(NSString *)text, ... {
 va_list args;
 va_start(args, text);
 NSLog(text, args);
}

The argument (or some argument) comes through, but it's got some weird value (output is blah blah 1606412704). How should I pass the values that come in via ...?

+2  A: 

You could use -[NSString initWithFormat:arguments:]:

- (void)log:(NSString *)text, ...
{
    va_list args;
    va_start(args, text);
    NSString *log_msg = [[[NSString alloc] initWithFormat:text arguments:args] autorelease];
    NSLog(@"%@", log_msg);
}
mipadi
Wow. While that doesn't answer my question, it solves my current problem. How would you pass the args to the next method, though?
Yar
You can't pass the variable parameters. You can pass `args`, but that means the function or method has to take an object of type `va_list`.
mipadi
Okay, that is very cool. I ended up doing this `- (void)log:(NSString *)text, ...;` and `- (void)log:(NSString *)text withArguments:(va_list)list;`
Yar
@yar as a general rule, whenever you find yourself writing a function or method that accepts ..., it's probably a good idea to also make a version that accepts a va_list and have the former invoke the latter. You get more flexibility that way. :)
Dave DeLong
+2  A: 

There's a variant of NSLog that accepts a va_list called NSLogv:

- (void) log:(NSString *)text, ... {
  va_list args;
  va_start(args, text);
  NSLogv(text, args);
  va_end(args);
}

The only way to forward the actual ... (not the va_list) is to use a macro. For example:

#define MyLog(f,...) { \
NSLog(f,##__VA_ARGS); \
[someObject doSomething:f, ##__VA_ARGS]; \
}

However, this should be used very sparingly, since macros can make code really obfuscated.

Dave DeLong
That is cool and +1, but I still don't know how to pass the args to the next method (or maybe I am doing it right?)
Yar
You can't. Once `...` is converted to `va_list`, it can be only passed to a function or a method which accepts `va_list`, not to a function/method which accepts a variable number of parameters.
Yuji
@Yuji, ooooooooooooooooo lemme try that out.
Yar
Thanks @Yuji, that worked. So there's no way to pass the variable number of params (in other words, I MUST convert it to a va_list)? Also, if you want to put your comment in an answer, that would help.
Yar
@yar you can only forward the `...` in non-`va_list` form inside macros. (`##__VA_ARGS__`) (Edited answer w/ more info)
Dave DeLong
Thanks. The macros are even more obfuscated by SO's formatting above. So that's pretty cool. I should've asked the questions in a more generic way, to get better more generic answers. What I'm really interested in is how to get and pass `...`. I get it now, though: the method that gets it can only pass it as a va_list or using macros, which is bad practice.
Yar
@yar the `##__VA_ARGS` only applies to the list of items passed into the macro, not passed into the function/method. Once they're inside a function/method, the only way to extract them is via a `va_list`.
Dave DeLong
@yar Indeed. That's why there's a whole series of functions with and without `v` in the standard C library, like `printf` and `vprintf`.
Yuji
Thanks again Dave DeLong and @Yuji. I'm learning C accidentally via Objective-C, so most of this stuff is news to me.
Yar