Thread: va_list issues

  1. #1
    Registered User
    Join Date
    Sep 2006
    Posts
    24

    va_list issues

    So, I have a function that accepts a variable number of arguments and va_start's a va_list. It then creates a struct, assigns that va_list to one of the members, and then stores that struct globally. An undetermined time later, a function in that struct is called with the va_list, va_end is called at the end of it, and then the object is destroyed. My problem is that the function that creates the va_list does not seem to be creating a new one each time it is called... For example:

    The struct initializing function is called with (15,2), then it is called again with (3,4). When the two struct's functions are later executed, the data used is 3, 4 for both of them. What's happening, and, more importantly, how do I fix it?

    This function creates the task:
    Code:
    void new_task(double time_till_execution, void (*function)(va_list), ...)
    {
        // Argument list generation
        va_list arguments;
        va_start(arguments,function);
        
        // Instance initialization
        task task_p;
        task_p.func = function;
        task_p.args = arguments;
        queue.push(task_p);
    }
    This function executes the task:
    Code:
    void task_execute(task const t) {      // Function to execute the task
         t.task(t.args);
    }
    And this is an example of a function called:
    Code:
    void func_1(va_list arguments)
    {
        int x = va_arg(arguments,int);
        int y = va_arg(arguments,int);
        cout<< x << " " << y << "\n\n";
    }
    Last edited by LPP; 10-19-2006 at 11:48 AM.

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    > task_p.args = arguments;
    This is just a POINTER to your arguments, it is not a complete copy of everything.
    So when the function returns, the pointer is no longer valid.

    > how do I fix it?
    Maybe use a std::vector for passing your arguments rather than that horrid varargs approach (which is plain messy in C, nevermind C++).
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Registered User
    Join Date
    Sep 2006
    Posts
    24
    Quote Originally Posted by Salem
    This is just a POINTER to your arguments, it is not a complete copy of everything.
    So when the function returns, the pointer is no longer valid.
    So, a pointer issue... Would I be able to, say, malloc arugments to keep that from happening? (Don't hurt me!)

    Quote Originally Posted by Salem
    Maybe use a std::vector for passing your arguments rather than that horrid varargs approach (which is plain messy in C, nevermind C++).
    Can't have that... I need to allow the user to input a variable number of arguments of different types that the end function is expecting.

  4. #4
    Registered User
    Join Date
    Sep 2006
    Posts
    24
    Another thing... Where should the va_end be called in my example (assuming there were no pointer issues)? Should it be called in the constructor, or in the end function?

  5. #5
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    > I need to allow the user to input a variable number of arguments of different types that the end function is expecting.
    Your example use of the va_args does not show an example of how to determine how many additional parameters there are.
    func(1,2);
    func(1,2,3,4,5,6);
    are indistinquishable at the va_arg level, you need some additional way of telling how many arguments there are.
    printf()/scanf() use the number of % conversions
    exec() uses a NULL pointer

    Even if you could malloc, it would still only be a shallow copy
    func(1,2,mystring,22);
    You'd only copy the pointer for mystring, not what it was pointing to (again, more problems for your deferred execution model).

    > Where should the va_end be called in my example
    Probably at the end of the function which called va_start()
    The va_ macros may be declared as containing braces, so it is actually invalid to have the start and end in different scopes.

    To be honest, forget about extracting parameters from the user input before the call and just pass everything as a string. Then let the called function parse out the parameters and validate that enough correct input was provided. It will certainly save an awful lot of mess.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  6. #6
    Registered User
    Join Date
    Sep 2006
    Posts
    24
    Quote Originally Posted by Salem
    Your example use of the va_args does not show an example of how to determine how many additional parameters there are.
    func(1,2);
    func(1,2,3,4,5,6);
    are indistinquishable at the va_arg level, you need some additional way of telling how many arguments there are.
    printf()/scanf() use the number of % conversions
    exec() uses a NULL pointer

    Even if you could malloc, it would still only be a shallow copy
    func(1,2,mystring,22);
    You'd only copy the pointer for mystring, not what it was pointing to (again, more problems for your deferred execution model).
    Well, you've shown that I can't do it through malloc, and I also can't use formatting strings or a null marker to signal the end of a va_list because I need to keep it in that form until it gets to the end function (func_1 knows what data it will be receiving in the form of a va_list).
    Quote Originally Posted by Salem
    Probably at the end of the function which called va_start()
    The va_ macros may be declared as containing braces, so it is actually invalid to have the start and end in different scopes.
    Would the memory location of the va_list be preserved if I did not call va_end until I was done with the data? I know that multiple va_lists can be "active" at the same time...
    Quote Originally Posted by Salem
    To be honest, forget about extracting parameters from the user input before the call and just pass everything as a string. Then let the called function parse out the parameters and validate that enough correct input was provided. It will certainly save an awful lot of mess.
    The end user is a programmer that will actually be passing objects and stuff to these functions. Tasks can also spawn other tasks, so string formatting is not a great option. I think that va_lists are really what I need to use... I just need a way to preserve them across functions.

  7. #7
    Registered User
    Join Date
    Sep 2001
    Posts
    752
    The function called (in my example, would be func_1) would know what inputs it should be receiving. I'm not sure if there's a better way to do this, because the task struct should be infinitely flexible with regards to what function it is passed and what arguments it is called with. Your case with the string shows me that, even if I were to use a NULL marker or a format string to determine the size of the values, I would still not be able to preserve all of the data through malloc. How is the data then kept from being overwritten when it is in the same scope (ie. the function that creates the argument list)? If I were to not call va_end in that function, would the memory not be freed up?
    Variable length argument lists are just data on the stack. va_start returns a pointer to data on the stack. va_end does not free the memory, it just resets the pointer to NULL. The memory is freed when you leave the function's scope.


    The typical way to do what you are trying to get done with void *.
    The C++ way to do this however, is using virtual functions.
    Code:
    void new_task (double time_till_execution, void (*fn) (void *), void * data) {
       task task_p;
    
       task_p.func = fn;
       task_p.args = data;
       queue.push(task_p);
    }
    
    void task_execute (task t) {
       t.func(t.args);
    }
    
    void func_1 (void * data) {
       intPair * ip = data;
    
       cout << ip->x << " " << ip->y << "\n\n";
    }
    
    struct IntPair {
       int x;
       int y;
    };
    
    void foo (void) { // Example of how to use new_task
       IntPair * myPair = new IntPair;
       myPair->x = 5;
       myPair->y = 14;
    
       new_task (180, func_1, myPair);
    }
    Callou collei we'll code the way
    Of prime numbers and pings!

  8. #8
    Registered User
    Join Date
    Sep 2001
    Posts
    752
    C++ Style
    Code:
    class task {
    public:
       virtual void execute() = 0;
    };
    
    class fn_1 : public task {
    public:
       int x;
       int y;
       void execute() { cout << x << " " << y << "\n\n"; }
    };
    
    void new_task (double time_till_execution, task * addMe) {
       queue.push (addMe);
    }
    
    void task_execute (task * t) {
       t->execute();
    }
    
    void foo (void) { // Example of how to use new_task
       fn_1 myfn = new fn_1;
       myfn->x = 5;
       myfn->y = 14;
    
       new_task (180, myfn);
    }
    Callou collei we'll code the way
    Of prime numbers and pings!

  9. #9
    Registered User
    Join Date
    Sep 2006
    Posts
    24
    I opted to try out templates... To see how that went (heh) check out my thread about the linker error. I'll try out the void* solution now, see how that goes... I'm optimistic, except for memory management. I realized that I absolutely have to use malloc() if I'm going to be passing any kind of list of arguments.

  10. #10
    Registered User
    Join Date
    Sep 2001
    Posts
    752
    I saw your template thread. I suspect it's not what you are really looking for....

    Every function in the queue will have to have the same paramaters... templates won't solve the problem of storing functions that take different amounts and types of data in a uniform manner.
    Callou collei we'll code the way
    Of prime numbers and pings!

  11. #11
    Registered User
    Join Date
    Sep 2006
    Posts
    24
    Ahah! But the user specifies a single struct that contains all the data they need for the end function. It looks like I am going to have to do a bit of void* manipulation, but I'm not sure how it will go.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Bitmap scroll issues
    By Gerread in forum Windows Programming
    Replies: 4
    Last Post: 05-14-2007, 05:18 AM
  2. Solution to culling issues?
    By VirtualAce in forum Game Programming
    Replies: 4
    Last Post: 03-14-2006, 06:59 PM
  3. Directx issues
    By dpro in forum Game Programming
    Replies: 7
    Last Post: 03-30-2005, 01:58 PM
  4. gphoto2 issues
    By axon in forum Tech Board
    Replies: 3
    Last Post: 03-21-2004, 08:33 PM
  5. hexdump issues
    By daluu in forum C Programming
    Replies: 2
    Last Post: 03-04-2003, 09:01 PM