How to declare a block function with variadic arguments in Objective-C? - 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;
};

Related

__VA_ARGS__ runtime equivalent?

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];
}

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];
}

string with format as argument for method (objective-c)

The [NSString stringWithFormat:]; can take multiple arguments even though it's declared as NSString not NSArray and there's only one colon.
How can I have this for my own method, which is like a replacement for NSLog that writes to a text field so it's used often and I don't want to keep adding more square brackets.
Use an ellipsis after your argument name:
(NSNumber *) addValues:(int) count, ...;
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocDefiningClasses.html
You then need to use va_list and va_start to iterate through the arguments provided:
- (NSNumber *) addValues:(int) count, ...
{
va_list args;
va_start(args, count);
NSNumber *value;
double retval;
for( int i = 0; i < count; i++ )
{
value = va_arg(args, NSNumber *);
retval += [value doubleValue];
}
va_end(args);
return [NSNumber numberWithDouble:retval];
}
Example from: http://numbergrinder.com/node/35
Note that this is a built-in C functionality, not part of Objective-C specifically; there is a good explanation of the va_arg usage here:
http://publications.gbdirect.co.uk/c_book/chapter9/stdarg.html

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 );