9

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 ...?

Community
  • 1
  • 1
Dan Rosenstark
  • 68,471
  • 58
  • 283
  • 421

2 Answers2

21

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.

phatblat
  • 3,804
  • 3
  • 33
  • 31
Dave DeLong
  • 242,470
  • 58
  • 448
  • 498
  • 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?) – Dan Rosenstark Jun 29 '10 at 20:11
  • 3
    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 Jun 29 '10 at 20:18
  • @Yuji, ooooooooooooooooo lemme try that out. – Dan Rosenstark Jun 29 '10 at 20:31
  • 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. – Dan Rosenstark Jun 29 '10 at 20:35
  • 1
    @yar you can only forward the `...` in non-`va_list` form inside macros. (`##__VA_ARGS__`) (Edited answer w/ more info) – Dave DeLong Jun 29 '10 at 20:43
  • 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. – Dan Rosenstark Jun 29 '10 at 20:53
  • 1
    @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 Jun 29 '10 at 21:00
  • @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 Jun 29 '10 at 21:58
  • Thanks again Dave DeLong and @Yuji. I'm learning C accidentally via Objective-C, so most of this stuff is news to me. – Dan Rosenstark Jun 30 '10 at 20:24
12

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
  • 398,885
  • 90
  • 523
  • 479
  • Wow. While that doesn't answer my question, it solves my current problem. How would you pass the args to the next method, though? – Dan Rosenstark Jun 29 '10 at 20:08
  • 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 Jun 29 '10 at 20:27
  • 1
    Okay, that is very cool. I ended up doing this `- (void)log:(NSString *)text, ...;` and `- (void)log:(NSString *)text withArguments:(va_list)list;` – Dan Rosenstark Jun 29 '10 at 20:37
  • 1
    @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 Jun 30 '10 at 04:21