Dynamically creating a va_list in C ...
... seems to be impossible, from what I can find out on the web.
A va_list is a variable argument list which you can define as an argument to a function (or a method in Objective-C). A prominent example of this is vprintf (see printf and variadic functions on Wikipedia). In Apple's Cocoa library, one example is NSString's initWithFormat:arguments:.
My problem was this: I want to call this method, but I don't know at compile-time which arguments I want to pass. Instead, I create an array with arguments. Now, I thought I would be able to create a va_list from this array and pass it to initWithFormat:arguments:, but, as noted above, this is apparently not possible. So, to make a long story short, I had to conjure up my own mock-up of such a format function. I only need to deal with strings, so basically I scan for occurrences of "%@" (indicates an object in Objective-C) and replace them with the elements from the array. Not very elegant, but it works.
- (NSString *)evaluatePseudoFormat: (NSString *)format
withArguments: (NSArray *)array
{
NSMutableString *evaluatedString =
[NSMutableString stringWithString: format];
NSRange varRange, scanRange;
int length = [format length];
scanRange = NSMakeRange(0, length);
int index = [array count];
NSString *replacement;
while ((varRange = [format rangeOfString: @"%@"
options: NSBackwardsSearch
range: scanRange]).length > 0 && index >= 0) {
replacement = [array objectAtIndex: --index];
[evaluatedString replaceCharactersInRange: varRange
withString: replacement];
length = varRange.location;
scanRange = NSMakeRange(0, length);
}
return evaluatedString;
}
Don't ask me why I search backwards. I had some complicated reason for that. After changing some things, the reason became obsolete. However, since the code works, I don't see why I should change it now. ;-)



1 Comments:
It is possible to forge a va_list. It's simply a packed array; for example, a char followed by an int would be a total of five bytes long (assuming four-byte int).
So, something like this:
union {
va_list varargs;
void *packedArray;
} myFakeArray;
void *ptr = myFakeArray.packedArray = alloca(5); //See x-man-page://alloca
*(char *)ptr = myChar;
ptr += sizeof(char);
*(int *)ptr = myInt;
vprintf("Character %c; int %i\n", myFakeArray.varargs);
The C standard doesn't actually define what a va_list is, though, so you should probably file this solution under “non-portable fragile black magic”. Thus, while the above is true of Mac OS X today, it may vary on some other OS or some future version of OS X.
Post a Comment
<< Home