__VA_ARGS__ runtime equivalent? - objective-c

I'm trying to make a function similar to this:
#define printf_copy(s, ...) printf(s, ##__VA_ARGS__) // acceptable!
but that's a preprocessor, I need one for runtime, like this:
+ (NSString *)format:(NSString *)first, ...
{
return [NSString stringWithFormat:first, __VA_ARGS__]; // unacceptable!
}
BUT!! this is unacceptable by compiler!
I'm trying to figure out whats the local variable for (...)?
(yes those 3 dots)

It's exactly the same as with C variadic functions. That means you can't just pass it through directly, you have to pass a va_list around. You'll need something like:
+ (NSString *)format:(NSString *)first, ...
{
NSString *string;
va_list args;
va_start(args, first);
string = [[NSString alloc] initWithFormat:first arguments:args];
va_end(args);
return [string autorelease];
}

Related

How to declare a block function with variadic arguments in Objective-C?

How to declare a block function with variadic arguments in Objective-C?
NSString *(^fn)(va_list) = ^(...) { // ?
};
You have two problems:
A va_list argument is not the same as a ... argument.
A variadic C function must have at least one named argument.
Here's an example that compiles:
NSString *(^fn)(NSString *format, ...) = ^NSString *(NSString *format, ...){
va_list ap;
va_start(ap, format);
NSString *answer = [[NSString alloc] initWithFormat:format arguments:ap];
va_end(ap);
return answer;
};

Functionality of sprintf for NSString

How can I get the functionality of sprintf in Objective-C? The function is of course part of stdio in C, so I could certainly invoke it, but since I'm using Foundation, I need it to work with NSStrings as well.
EDIT
I apologize for my inarticulateness, but I'm actually hoping for something more like the PHP sprintf function that returns a string. (This was perhaps slightly evident before Josh Caswell's very efficient edit of my question.) It would be like NSLog but instead of writing to console would give a string (or pointer) as a return value.
The closest function that does the same thing in Cocoa is NSString's stringWithFormat.
Using sprintf:
char buf[100];
sprintf(buf, "Line %d of %d", currentLine, totalLines);
Using stringWithFormat:
NSString res = [NSString stringWithFormat:#"Line %d of %d", currentLine, totalLines];
Note that stringWithFormat: supports printing of Cocoa objects with %# format specifier.
One option:
NSString *OCSprintf(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
NSString *OCSprintf(NSString *format, ...)
{
va_list args;
va_start(args, format);
NSString *result = [[[NSString alloc] initWithFormat:format arguments:args] autorelease];
va_end(args);
return result;
}
This just wraps stringWithFormat: (more precisely, one of its cousins) as a function as you requested. The NS_FORMAT_FUNCTION(1,2) ensures you still get format string checking.
NSString * myString = [NSString stringWithFormat:#"...", params…];
const char * myStrPtr = [myString UTF8String];

How to customize a method like 'stringWithFormat'?

I want to customize a method with formated string input and (const char *) return,but the problem is like below... Can anyone tell me how to resolve it? Thanks.
-(const char *)stringWithFormat:(NSString *)format, ...
{
va_list args;
va_start(args, format);
NSString *lString = [[NSString alloc] initWithFormat:format arguments:args];
[lString autorelease];
va_end(args);
return [lString cStringUsingEncoding:NSUTF8StringEncoding];
}

Printing an NSString

What's the correct way to print an NSString in Objective-C? A lot of examples use NSLog(), but according to the documentation:
NSLog is a FoundationKit function for printing debug statements to the console.
...
NSLog works basically like:
fprintf(stderr, format_string, args ...);
Which to me is a bit like the _TRACE macro in Win32/C++. I don't want to print to stderr, I want to print to stdout. There are people who suggest using printf() as follows:
printf("%s", [str cStringUsingEncoding:NSUTF8StringEncoding]);
But this seems like an extra level on indirection to get the NSString printed, and it doesn't "feel" like the solution.
Spewing stuff to stdout is actually a pretty rare thing to do in Cocoa, given that almost all projects are GUI in nature. There are relatively few projects that are built as command line tools or otherwise need to deal with stdout.
However, the Foundation does provide the means to write to stdout. Specifically, NSFileHandle has fileHandleWithStandardOutput which gives you a file handle that can write to stdout.
From there, it is a matter of converting the NSString to an NSData and writing it.
Quite a few steps, but easily wrapped up in a reusable function:
void MyLog(NSString *format, ...) {
va_list args;
va_start(args, format);
NSString *formattedString = [[NSString alloc] initWithFormat: format
arguments: args];
va_end(args);
[[NSFileHandle fileHandleWithStandardOutput]
writeData: [formattedString dataUsingEncoding: NSNEXTSTEPStringEncoding]];
[formattedString release];
}
Well this is the solution.
Since printf is a pure C function, it won't recognize the Objective-C objects. (NSLog's formatter is distinct from printf's one.) Therefore, you have to convert it into a C string before formatting.
BTW you can use [str UTF8String] instead of [str cStringUsingEncoding:NSUTF8StringEncoding].
C string (UTF8String) is a pointer to a structure inside the string object.
NSString *str = #"Hello, World.";
printf("%s\n", [str UTF8String]);
I think you'll find these adequate for your needs:
// print to stdout
static void NSPrint(NSString *format, ...) {
va_list args;
va_start(args, format);
NSString *string = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
fprintf(stdout, "%s\n", [string UTF8String]);
#if !__has_feature(objc_arc)
[string release];
#endif
}
// print to stderr
static void NSPrintErr(NSString *format, ...) {
va_list args;
va_start(args, format);
NSString *string = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
fprintf(stderr, "%s\n", [string UTF8String]);
#if !__has_feature(objc_arc)
[string release];
#endif
}
You should use the custom file handler or write a macro yourself.
Tip: when NSLog prints out an object it uses object's debugDescription method. You could override this method for your custom NSObject subclasses to print custom debugInfo to stdout.
I just do:
define NSPrintf(...) printf( "%s", [[NSString stringWithFormat: __VA_ARGS__] UTF8String] )
Then i can use it as:
NSPrintf( #"Sorry %#, I can't do that\n", name );

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