storing variable arguments for future use

M

matevz bradac

Hi,

I'm trying to implement delayed function execution,
similar to OpenGL's display lists. It looks something
like this:

beginList ();
fcn1 ();
fcn2 ();
.
.
endList ();

The functions between beginList() and endList() are not
executed, but rather compiled to a list, which may be run
sometime in the future - by calling execList().

One of the fcn()s that can be used in the list should allow
the programmer to call an arbitrary function with variable
arguments (we know they are all long ints), e.g.:
void fcnX (void (*fcnToCall)(), long numArgs, ...)

When execList() is called, fcnToCall (numArgs, ...) should
be executed. The problem is, how do I store variable args
for future use? I know they're all long ints, so this should
be somehow helpful, but I can't figure it out. Do I have to
remember/copy the stack and is this possible in C, or must it
be done in assembly?

regards,
matevz
 
C

Christopher Benson-Manica

matevz bradac said:
When execList() is called, fcnToCall (numArgs, ...) should
be executed. The problem is, how do I store variable args
for future use? I know they're all long ints, so this should
be somehow helpful, but I can't figure it out. Do I have to
remember/copy the stack and is this possible in C, or must it
be done in assembly?

Have you looked at the functions in <stdarg.h>? They sound like
they'd help you a lot.
 
A

Alex

[snip]
When execList() is called, fcnToCall (numArgs, ...) should
be executed. The problem is, how do I store variable args
for future use? I know they're all long ints, so this should
be somehow helpful, but I can't figure it out. Do I have to
remember/copy the stack and is this possible in C, or must it
be done in assembly?

Assembly is probably the only way to do exactly what you describe,
unfortunately. Using the functions in <stdarg.h>, you could store a
suitably-sized array of the longs (when creating the function list), but
that wouldn't help much as there's no way in standard C to construct the
call to the variadic function.
 
M

Michael Wojcik

[snip]
When execList() is called, fcnToCall (numArgs, ...) should
be executed. The problem is, how do I store variable args
for future use?

Assembly is probably the only way to do exactly what you describe,
unfortunately. Using the functions in <stdarg.h>, you could store a
suitably-sized array of the longs (when creating the function list), but
that wouldn't help much as there's no way in standard C to construct the
call to the variadic function.

I couldn't tell from the OP's description, but this might be another
case where the problem can be solved by implementing the real function
as a non-variadic one with a variadic wrapper. That is:

int fcnToCallV(long numArgs, va_list Args);

int fcnToCall(long numArgs, ...)
{
va_list Args;
int Ret;

va_start(Args, numArgs);
Ret = fcnToCallV(numArgs, Args);
va_end(Args);

return Ret;
}

(The OP didn't mention a return type for these functions, so I assumed
int.)

Then the OP's call-list system stores a call to fcnToCall as one to
fcnToCallV, by doing the va_start before storing it and the va_end
when processing the list, after the call returns.
 
M

matevz bradac

This is exactly what I want. I was hoping there's an easy
way of, well, memcpy()-ing the stack, and then passing it
to some function... -> assembly it will have to be.

Thanks,
matevz
 
E

Eric Sosman

matevz said:
This is exactly what I want. I was hoping there's an easy
way of, well, memcpy()-ing the stack, and then passing it
to some function... -> assembly it will have to be.

I strongly recommend you consider alternatives, because
one consequence of resorting to assembly is that you tie
your program to one machine, possibly to one compiler on
that machine, and perhaps even to one version of one
compiler on that machine. That's quite a lot to give up,
and is seldom worth the sacrifice.

It is beyond the capabilities of C to call a function
when the number and types of the function arguments are
not known at compile time. However, if you're willing to
change the functions' interfaces you can accomplish your
goal in a slightly different way. C cannot build "varying"
argument lists, but it *can* build varying data structures,
and you can exploit the latter capability to get around
the former limitation.

Your original idea was

void fcnX (void (*fcnToCall)(), long numArgs, ...)

where all the `...' arguments are `long'. Actually, a
better declaration would have been

void fcnX (void (*fcnToCall)(long numArgs, ...),
long numArgs, ...)

to show that the called function receives an argument count
followed by the arguments themselves. My suggestion is that
you change the signature of the called function to accept
an argument count and an *array* of arguments:

void (*fcnToCall)(long numArgs, long Args[])

All your worries about non-portability have suddenly vanished:
the called function takes exactly two arguments, always, and
it's easy to use malloc() to build the array for the second
argument.

With this change to the called functions' signature, you
could take either of two approaches (perhaps more) for the
outer function:

void fcnX (void (*fcnToCall)(long numArgs, long Args[]),
long numArgs, long Args[])

void fcnY (void (*fcnToCall)(long numArgs, long Args[]),
long numArgs, ...)

The caller of fcnX() is now responsible for building the array,
and fcnX() just stashes the information away in whatever data
structure you're using to hold it. With the second approach,
the fcnY() caller builds the array from its own variable-length
argument list and then does whatever fcnX() would have done.
In fact, it might be useful to provide both functions, making
fcnY() call fcnX() after building the array. (One change I'd
suggest, though: since fcnY() will certainly call malloc() and
since malloc() can fail, fcnY() should have a way to report
the failure back to its caller. Depending on the "recording"
data structure, this may be a concern for fcnX() as well.)

Example (with all error-checking omitted for brevity and
because no error-reporting mechanism has yet been chosen):

void fcnY (void (*fcnToCall)(long numArgs, long Args[]),
long numArgs, ...) {
long *array = malloc(numArgs * sizeof *array);
va_list ap;
long n;
va_start (ap, numArgs);
for (n = 0; n < numArgs; ++n)
array[n] = va_arg(ap, long);
va_end (ap);
fcnX (fcnToCall, numArgs, array);
}

I think you'll find that the slight loss in flexibility
is more than worth the gain in portability and ease of
coding.
 
M

matevz bradac

Eric Sosman said:
Your original idea was

void fcnX (void (*fcnToCall)(), long numArgs, ...)

where all the `...' arguments are `long'. Actually, a
better declaration would have been

void fcnX (void (*fcnToCall)(long numArgs, ...),
long numArgs, ...)

to show that the called function receives an argument count
followed by the arguments themselves. My suggestion is that
you change the signature of the called function to accept
an argument count and an *array* of arguments:

void (*fcnToCall)(long numArgs, long Args[])

Well, I would if I could. However, fcnToCall() is not a function
provided with my library, but any external function that user provides.
Also the prototype is from the old SGI days when compilers were more
forgiving, I just copied the declaration, thus void (*fcnToCall)()

Regards,
matevz
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top