gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

C/C++ > How can I save va_list arguments for later use?

#175260 - Polaris - Mon Oct 25, 2010 11:04 pm

First and foremost, I don't even know if this will be half as useful as I think, so try not to be too harsh if you try to explain why this is a dumb thing to do :)

Ok, so this is what I'm trying to do.

1) I send some random amount of arguments to a function using ellipsis.
2) Then I read the va_list and store each value into a void pointer that are in turn saved into a vector.
3) My problem comes when I try to use the data in those void pointers, casting them to the proper data type doesn't seem to work.

Here is some sample code, for simplicity sake I'm only using int as data type.

Code:

#include <vector>
#include <iostream>
#include <stdarg.h>

struct Bla{
   void* ble;
};

std::vector<Bla*> va_function(int argAmount, ...){
   va_list vl;
   va_start(vl,argAmount);
   
   std::vector<Bla*> bli;

   for (int i=0;i<argAmount;i++){
      int val = va_arg(vl,int);
      Bla* item = new Bla();
      item->ble = &val;
      
      std::cout << *(static_cast<int*>(item->ble)) << std::endl;

      bli.push_back(item);

      std::cout << *(static_cast<int*>(bli[0]->ble)) << std::endl;
   }

   va_end(vl);

   std::cout << "BLABLABALBALBALBAL" << std::endl;

   std::cout << *(static_cast<int*>(bli[0]->ble)) << std::endl;
   std::cout << *(static_cast<int*>(bli[1]->ble)) << std::endl;
   std::cout << *(static_cast<int*>(bli[2]->ble)) << std::endl;
   std::cout << *(static_cast<int*>(bli[3]->ble)) << std::endl;

   return bli;
};

int _tmain(int argc, _TCHAR* argv[]){
   std::vector<Bla*> blih;

   blih = va_function(4, 5, 6, 7, 8);

   std::cout << "BLABLABALBALBALBAL" << std::endl;

   std::cout << *(static_cast<int*>(blih[0]->ble)) << std::endl;
   std::cout << *(static_cast<int*>(blih[1]->ble)) << std::endl;
   std::cout << *(static_cast<int*>(blih[2]->ble)) << std::endl;
   std::cout << *(static_cast<int*>(blih[3]->ble)) << std::endl;
 
        return 0;
}


The above prints the following

Code:

5
5
6
6
7
7
8
8
BLABLABALBALBALBAL
8
8
8
8
BLABLABALBALBALBAL
-858993460
-858993460
-858993460
-858993460


Any hints or help is appreciated.

Obviously there is something terribly wrong with accessing those values after calling va_end. I'll continue to look at it, if I stare it long enough I'm sure the answer will come to me.

#175261 - elhobbs - Tue Oct 26, 2010 1:14 am

Code:
      int val = va_arg(vl,int);
      Bla* item = new Bla();
      item->ble = &val;

&val is the address of a stack variable. it is not permanent - it is going out of scope.

why are you messing with vargs? just use the vector

#175264 - Polaris - Tue Oct 26, 2010 1:17 pm

As you put it, I'm just messing with C++, not really doing anything. Like poking a beast with a stick :)

I thought it would be pretty cool(as cool as coding can be) if I could hold a vector of void pointers with whatever data came in the va_list, and then do the proper cast later on when I needed it.

#175319 - kusma - Tue Oct 26, 2010 9:43 pm

As elhobbs pointed out, you're taking an address from the stack frame. That address will also be the same for ever element in the list. This is just horribly broken code.

Perhaps you're looking for va_copy()? This was introduced in C99 to allow to iterate through the same va_list multiple time. You're not really giving enough background here...

#175330 - sajiimori - Wed Oct 27, 2010 10:30 pm

Poking a beast with a stick! Hahah, well said. :)

It's easy to explain why the code doesn't work, but it's meaningless for us to give "suggestions" if you're just messing around without any particular goal.

#175339 - sgeos - Thu Oct 28, 2010 3:41 pm

Storing the void pointers doesn't make any sense (because they will be invalid as soon as the function returns), so you will need to figure out what those pointers point to and copy the data to a permanent location. You'll need to allocate space for the values you want to store, and then dereference the pointers to copy the parameters to a permanent memory location. This may involve dropping the values into a statically allocated memory pool, or dynamically allocating memory for the values.

FWIW, if you are passing consts with global scope, I think storing the void pointers should work.

#175341 - kusma - Thu Oct 28, 2010 6:24 pm

sgeos wrote:
FWIW, if you are passing consts with global scope, I think storing the void pointers should work.

No, he's reading them all into the same variable "int val" and taking the address of that, so it would most certainly not work :)

#175342 - Polaris - Thu Oct 28, 2010 8:39 pm

At last success!

I replaced this:

Code:
int val = va_arg(vl,int);
Bla* item = new Bla();
item->ble = &val;


with this:

Code:
int val = va_arg(vl,int);
Bla* item = new Bla();
item->ble = new int();
*(static_cast<int*>(item->ble)) = val;


I figured it out from reading this bit.

You'll need to allocate space for the values you want to store, and then dereference the pointers to copy the parameters to a permanent memory location.


Now with some not so crafty code I should be able to hold what ever values I send in through the ellipsis.

To put some background on this weird shit, I have done quite a bit of AS3 coding lately(hint!). Long story short, AS3 let's you do all sorts of strange crap with ease, and among that is it's implementation of something very similar to ellipsis, Adobe calls it the rest parameter.

What it does is turn what ever you send in through the function into an array with all the values you sent in, in the order you sent them in. So I wanted to know how would something like that would work in C++;

#175343 - sajiimori - Thu Oct 28, 2010 10:42 pm

And now you're about to discover that va_function needs to take some kind of parameter that specifies the types of the arguments. :)

#175344 - sgeos - Thu Oct 28, 2010 11:07 pm

sajiimori wrote:
And now you're about to discover that va_function needs to take some kind of parameter that specifies the types of the arguments. :)

Or you could build an assumption into your function call. Ie, I'm passing in indices for accessing values in my big lookup table of wonderful stuff.

#175348 - elwing - Fri Oct 29, 2010 7:51 am

sajiimori wrote:
And now you're about to discover that va_function needs to take some kind of parameter that specifies the types of the arguments. :)


that is only true if you want to use the vsprintf style of methods, otherwise you can make assumption on the type of the parameter in va_list (through if you know by advance the type passed that almost cancel all interest of using elipsis...)

#175350 - keldon - Fri Oct 29, 2010 10:39 am

Polaris wrote:
At last success!

I replaced this:

Code:
int val = va_arg(vl,int);
Bla* item = new Bla();
item->ble = &val;


with this:

Code:
int val = va_arg(vl,int);
Bla* item = new Bla();
item->ble = new int();
*(static_cast<int*>(item->ble)) = val;


I figured it out from reading this bit.

You'll need to allocate space for the values you want to store, and then dereference the pointers to copy the parameters to a permanent memory location.


Now with some not so crafty code I should be able to hold what ever values I send in through the ellipsis.

To put some background on this weird shit, I have done quite a bit of AS3 coding lately(hint!). Long story short, AS3 let's you do all sorts of strange crap with ease, and among that is it's implementation of something very similar to ellipsis, Adobe calls it the rest parameter.

What it does is turn what ever you send in through the function into an array with all the values you sent in, in the order you sent them in. So I wanted to know how would something like that would work in C++;


Hmm, food for thought ...
Code:
cout << 1 << "text" << myObject << endl;


But do try to stick to c++ practices when coding in c++; not because c++ is better, but so that you can learn to think and work in c++.

Consider this:
Code:
Derived a( 10 ), b( 20 ), c( 40 ), d( 50 );
Base *list[] = { &a, &b, &c, &d, Base::end };
someFunction( list );


Or:
Code:
someFunction( Base::List( ).add( a ).add( b ).add( c ).add( d ) )


Keep your mind in AS3 and you'll feel limited when you migrate to a new language.

#175353 - sajiimori - Fri Oct 29, 2010 6:39 pm

sgeos and elwing, I was assuming that Polaris ultimately wanted to have a heterogeneous list, hence the void pointers.

#175354 - Polaris - Fri Oct 29, 2010 6:55 pm

sajiimori wrote:
sgeos and elwing, I was assuming that Polaris ultimately wanted to have a heterogeneous list, hence the void pointers.


You got it right sajiimori, I wouldn't have bothered with void* otherwise.

Thanks for the input everyone.

#175357 - elhobbs - Fri Oct 29, 2010 8:42 pm

I almost never use ... style parameters - except when I want to mimic and/or override printf style printing. aside from that it is very limiting in that the parameter count can only change in the source code - it is not dynamic.

#175358 - sgeos - Sat Oct 30, 2010 4:23 am

I think keldon made a very good point, and I also agree with elhobbs.

I suppose a person could have reasons for holding lists of void pointers, but it seems dangerous enough that the reason ought to be very good. Proceed at your own risk. =)

In general, I think you are probably better off passing some kind of generic list (like an array of pointers) or a struct/class custom tailored to your specific problem (or a combination of both). Depending on what you are doing, dynamic behavior that can be data driven or scripted is good stuff.