Rss

Fun with varargs

For reasons you don’t need to know about (yet), I wanted to get my usual crutch of a logging UITextview implemented in plain C.

I hadn’t wanted to mess with varargs, so I usually write an Obj-C method like this:


-(void) screenLog:(NSString*) s {
	textView.text = [NSString stringWithFormat:@"%@%@n",
		textView.text, s];
}

What this does is to create an autoreleased NSString built from a format that’s just two strings concatenated together — the current contents of the text view and the argument string — and a new-line character. It then sets this new string as the new text of the UITextView

It sucks a little bit to call, because you have to pass in an NSString, not the usual varargs you’d use with NSLog. So to do:

NSLog (@"Current age: %d", 41);

you’d have to build the string up-front, like this:

[self screenLog: [NSString stringWithFormat: @"Current age: %d", 41]];

So, kind of annoying, but still useful when you want to log to the screen instead of standard out, like I’ve had to do this week while doing some Bonjour stuff between multiple devices scattered about the office, at most one of which gets to log to Xcode’s console. Yesterday’s post, with onscreen output of the two devices getting each other’s test message, shows why this is a nice crutch to have for experiments, prototypes, and throwaways.

Anyways, I actually wanted to do this with plain ol’ C, and happened across Matt Gallagher’s great write-up of varargs in Cocoa. Combining that with the realization that NSString has some method signatures that take a va_list, I was able to rewrite my screen logger in plain ol’ C:

void LogToUITextView (UITextView *view, NSString* format, ...) {
	va_list args;
	va_start (args, format);
	NSString* appendedText = [[NSString alloc]
				initWithFormat: format arguments: args];
	view.text = [NSString stringWithFormat:
				 @"%@%@n", view.text, appendedText];
	[appendedText release];
}

Calling it feels a lot more like calling NSLog:

- (void)viewDidLoad {
    [super viewDidLoad];

	// customize point
	LogToUITextView(textView, @"Current age: %d", 41);
	LogToUITextView(textView, @"Current weight: %3.1f", 243.6);
	LogToUITextView(textView, @"Available fonts:n %@",
				[UIFont familyNames]);
}

And check it out: it actually works:

varargs-logging-function

I’ll probably adapt the varargs approach in my Obj-C logging function going forwards, but still, it’s nice to be able to make the procedural C call, especially since you could switch all NSLog calls to LogToUITextView with a single global replace.

Update: Here’s an even “more C” version that’s functionally equivalent:

void LogToUITextView (UITextView *view, NSString* format, ...) {
	va_list args;
	va_start (args, format);
	CFStringRef appendedText = CFStringCreateWithFormatAndArguments (
		kCFAllocatorDefault,
		NULL,
		(CFStringRef) format,
		args);
	CFStringRef newText = CFStringCreateWithFormat (
		kCFAllocatorDefault,
		NULL,
		(CFStringRef) @"%@%@n",
		view.text,
		appendedText);
	view.text = (NSString*) newText;
	CFRelease (newText);
	CFRelease (appendedText);
}

Obviously wordier, and we lose a convenient autorelease, since CoreFoundation doesn’t have autoreleasing.

Previous Post

Next Post

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.