Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the proper way to wrap [NSString stringWithFormat:]?

Assume I have a method with the signature:

+ (NSString *) myFormattedString:(NSString *)format, ...;

And I want it to prepend a string of my choice (e.g. @"Foo: "). I guess the best way is to use [myString initWithFormat:arguments:], but how would you implement this method?


I tried doing the following, but I get the warning as specified in the comment:

+ (NSString *) myFormattedString:(NSString *)format, ... {
  char *buffer;
  [format getCString:buffer maxLength:[format length] encoding:NSASCIIStringEncoding];

  va_list args;
  va_start(args, buffer); // WARNING: second parameter of 'va_start' not last named argument

  NSString *str = [[NSString alloc] initWithFormat:format arguments:args];
  [str autorelease];

  return [NSString stringWithFormat:@"Foo: %@.", str];
}

The reason I'm assuming va_start() can take in a (char *) is because of the example I saw on the manual page of STDARG(3). Feel free to completely rewrite the method if I'm doing it totally wrong.

like image 259
Senseful Avatar asked Jul 30 '09 21:07

Senseful


2 Answers

I think what you want is something like:

+ (NSString *) myFormattedString:(NSString *)format, ... {
  va_list args;
  va_start(args, format);
  NSString *str = [[[NSString alloc] initWithFormat:format arguments:args] autorelease];
  va_end(args);
  return [NSString stringWithFormat:@"Foo: %@.", str];
}

The stdarg.h va_* macros are used when a function (or, in this case, method) takes a variable number of arguments, as specified by "...". va_start() is used to find the start of where the variable number of arguments begins. As such, it needs to know a functions/methods last argument (the one just before the "...") in order to determine where the variable number of arguments starts. This is a somewhat simplified explanation since what actually happens under the hood is very ABI/Compiler specific. The most important point is that the second argument to va_start() is always the name of the variable 'just before the "..."'.

va_end() should be "called" (it's really a macro, not a function) for maximum portability. Again, this whole variable arguments thing is deep, deep black magic. Depending on the specifics of the compiler and ABI, va_end() may not do anything at all. On the other hand, failure to use va_end() may cause your program to crash when the return statement is reached because the stack frame (if there even is one) is no longer properly set up to actually perform a return.

like image 64
johne Avatar answered Sep 19 '22 16:09

johne


You've almost got it; just a couple of tweaks:

+ (NSString *) myFormattedString:(NSString *)format, ... {
  va_list args;
  va_start(args, format);

  NSString *str = [[NSString alloc] initWithFormat:format arguments:args];
  [str autorelease];

  va_end(args);

  return [NSString stringWithFormat:@"Foo: %@.", str];
}

That should do what you're looking for.

like image 28
Dave DeLong Avatar answered Sep 20 '22 16:09

Dave DeLong