Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I modify a va_list before passing it on?

In my attempts to understand what I can and can't do with a va_list in (Objective-)C, I came across this little puzzle. I was hoping to create a category on NSString that would simplify the stringWithFormat: message a bit in some cases, just for the fun of it. What I was aiming for was being able to use the implementation like this:

[@"My %@ format %@!" formattedWith:@"super", @"rocks"];

Hoping to end up with a string saying "My super format rocks!". My (incorrect) method implementation looks like this:

- (NSString *)formattedWith:(NSString *)arguments, ...
{
    va_list list;
    va_start(list, arguments);
    NSString *formatted = [[[NSString alloc] initWithFormat:self arguments:list] autorelease];
    va_end(list);
    return formatted;
}

Now the problem is that as soon as va_start() is called, the va_list is 'shortened' (for lack of a better word) and only contains the rest of the arguments (in the case of the example only @"rocks" remains, plus the calling object which I don't care about). What's passed onto the initWithFormat: message therefore renders the wrong kind of result.

On to the question. Are there ways to modify the va_list before I pass it to the initWithFormat: message, so I can somehow shift the first argument back onto the list?

I'm not looking for an iterative process where I loop through the va_list myself, I'm looking to understand the limits of va_list as a whole. Thanks!

like image 854
epologee Avatar asked Oct 03 '11 14:10

epologee


People also ask

How does va_ list work?

In the most usual stack-based situation, the va_list is merely a pointer to the arguments sitting on the stack, and va_arg increments the pointer, casts it and dereferences it to a value. Then va_start initialises that pointer by some simple arithmetic (and inside knowledge) and va_end does nothing.

What is Stdarg h header file used for?

The stdarg. h header file defines macros used to access arguments in functions with variable-length argument lists. The stdarg. h header file also defines the structure va_list .

What is a va list in C?

va_list is a complete object type suitable for holding the information needed by the macros va_start, va_copy, va_arg, and va_end. If a va_list instance is created, passed to another function, and used via va_arg in that function, then any subsequent use in the calling function should be preceded by a call to va_end.

How many macros are defined in the header file Stdarg h?

The <stdarg. h> header file declares a type and defines three macros for advancing through a list of function arguments of varying number and type.

Can you pass a VA_list to a variadic function?

Unfortunately, you can pass va_list to a variadic function, but you really do not want to because it most likely does not do what anyone wants. The compiler could/should probably detect that and print a warning... Well, @MarcH, that's the "you cannot hold the basketball and walk around" argument. Of course you can, but the ref won't let you.

How to initialize a_list with va_start and va_arg?

So in the function a_function, to initialize a_list with va_start, you would write va_start ( a_list, x ); int a_function ( int x, ... ) va_arg takes a va_list and a variable type, and returns the next argument in the list in the form of whatever variable type it is told. It then moves down the list to the next argument.

What is VA_pass in C++?

Like the original, va_pass just fetches a 256-byte block of memory from the parameter stack of the current variadic function (here, my_printf ()); it is then pushed onto the stack for the variadic function we are calling (in this case, printf ()).

How to initialize a list with a variable number of arguments?

For example, the following code declares a list that can be used to store a variable number of arguments. va_start is a macro which accepts two arguments, a va_list and the name of the variable that directly precedes the ellipsis ("..."). So in the function a_function, to initialize a_list with va_start, you would write va_start ( a_list, x );


1 Answers

The va_list can not be modified safely. The va_start argument also requires an argument to start the list which is excluded. The ways to work around this are to either pass an additional useless argument or use variadic macros.

//Method declaration
-(NSString*)formattedWith:(NSString*)ignored,...;

//Opt 1 (pass anything for first value)
[@"My %@ format %@!" formattedWith:nil, @"super", @"rocks"];

//Opt 2 (create a macro for the arguments)
#define VA_OPT(...) nil, ##__VA_ARGS__ //VARIADIC OPTIONAL
[@"My %@ format %@!" formattedWith:VA_OPT(@"super", @"rocks"];

//Opt 3 (just create a macro for the whole string format)
//Obviously you should just use the NSString method directly before resorting to this
#define FORMATTED_NSSTRING(str, ...) [NSString stringWithFormat:str, ##__VA_ARGS__]
FORMATTED_NSSTRING(@"My %@ format %@!", @"super", @"rocks")
like image 69
Joe Avatar answered Oct 22 '22 22:10

Joe