0

I have main update where I do access control before call. I would like to use one place in my code where call any urgent funcs.

I have a struct ACTION:

{
    FUNC_PROTOTYPE pfnAction;
    unsigned int   argsnum;
    va_list        argsval;
};

When I need invoke func, I do:
1 Put func and arguments to queue; 2 Pop them on the next update

{
    ACTION action;

    while(!Queue_isEmpty())
    {   // check and pop urgent functions
        if(Queue_Pop(&action))
        {
            action.pfnAction(action.argsnum, action.args);
            va_end(action.args);
        }
    }
}

For example, I try to call

void func(unsigned int argsnum, va_list args)

But my args inside func is corrupted.

I thought, that problem while I'm pop from queue:

Queue_Pop(P_ACTION p_res)
{
    if(!Queue_isEmpty())
    {
       p_res->pfnAction = header->pfnAction;
       p_res->argsnum = header->argsnum;

       if(0 < p_res->argsnum)
       {
           p_res->argsval = header->argsval;
           va_end(header->argsval);
       }
       ...
    }
}

But action.args is fine.

Bob
  • 57
  • 2
  • 8
  • Pretty sure you cannot save the `va_list` in a struct. Just use a lambda. – nwp Oct 19 '15 at 20:18
  • your code won't compile. There is no args member. You don't show the call to va_start(). – AShelly Oct 19 '15 at 20:18
  • sounds like you will have to write some assembler code – pm100 Oct 19 '15 at 20:40
  • @AShelly va_start() I do on queue_push(): `tail->pfnAction = p_pAction; tail->argsnum = p_pargs; va_start(tail->args, p_pargs);` In pop() I copy va_list and then use va_end. I would like to use single handler for any urgent funcs. – Bob Oct 19 '15 at 20:48
  • The argument list is stored on the stack and will get trashed once you return to the caller. I suppose this scheme for deferred execution might work if you recursed on every registration but I suspect the limitations on the flow-control would far outweigh any benefits from manually packing the argument lists into unions. Especially considering that you've got to mess-around with manual `va_arg` extraction anyway. – doynax Oct 19 '15 at 20:51

2 Answers2

0

You can't pass a vararg list around as an object, not even with va_list. That just doesn't work / is Undefined Behavior.

If you're programming in C++, then (1) don't tag your questions with C and (2) store a std::function with bound arguments.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Unfortunetly, I use pure C. I don't have any chance to use single point to call funcs with variable arguments, isn't it? – Bob Oct 19 '15 at 20:36
  • @bob: Nope. Only a C++ compiler can synthesize all the different calls. – MSalters Oct 19 '15 at 20:43
  • Ahem. `vprintf` exists. That is all. – FeepingCreature Jun 14 '18 at 19:16
  • 1
    @FeepingCreature: By virtue of being a Standard Library function, so whatever goes wrong when passing a va_list has to be undone by `vprintf`. [You can't do that](https://stackoverflow.com/questions/150543/forward-an-invocation-of-a-variadic-function-in-c) – MSalters Jun 14 '18 at 19:20
0

As @mSalters says, you can't pass va_list around - it is only valid while in the stack frame of the function with variable args.

On an architecture which strictly passes on the stack, you might be able to hack it by storing the initial address pointed to by va_list, and the address after walking through all expected arguments, then copying all the memory between the two addresses into an allocated buffer. This would have to be done before you queued it. On the queue pop side, you would fake out va_arg, by making va_list to point to your copy.

I wouldn't recommend actually doing this for any purposes other than experimentation.

And on many modern architectures, this won't work, as some variables are also passed in registers. See this article for details on how messy va_args can be.

AShelly
  • 34,686
  • 15
  • 91
  • 152