Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Calling a function with a dynamic number of parameters?

1 view
Skip to first unread message

dcbr...@gmail.com

unread,
May 25, 2005, 6:04:47 AM5/25/05
to
Guys,

I need to call a function that accepts a variable number of parameters
and I will not know how many parameters I have until run time.

Is it possible to build up the call somehow and then execute it?

I've heard of 'eval' but I've not found any documentation and I'm
wondering if that's a red herring.

Regards,
Dave.

Mike Caetano

unread,
May 25, 2005, 7:38:32 AM5/25/05
to
> I need to call a function that accepts a variable number of parameters
> and I will not know how many parameters I have until run time.
> Is it possible to build up the call somehow and then execute it?

If the parameters are the same type, consider passing a variable length
array and the number of elements in the array instead. For example:

void funk(type *array,int elements);

> I've heard of 'eval' but I've not found any documentation and I'm
> wondering if that's a red herring.

That sounds more like something from java.

-Mike

John

unread,
May 25, 2005, 11:19:19 AM5/25/05
to

Here's an example of using the variable args macros.

All the optional arguments sould have the same type, int, in this example.

#include <stdio.h>
#include <stdarg.h>

int _cdecl average( int numArgs, ... )
{
int sum = 0, i, value;
va_list arg_list;

va_start( arg_list, numArgs ); /* Initialize arguments. */
for (i = 0; i<numArgs; i++)
{
value = va_arg( arg_list, int);
sum += value;
}
va_end( arg_list ); /* Reset arguments. */
return( sum ? (sum / numArgs) : 0 );
}

int main(void)
{
printf("%d\n", average(3, 20, 30, 40));
return 0;
}

John

Antoine Leca

unread,
May 25, 2005, 12:27:04 PM5/25/05
to
En <news:1117015487....@g44g2000cwa.googlegroups.com>,

> I need to call a function that accepts a variable number of parameters
> and I will not know how many parameters I have until run time.
>
> Is it possible to build up the call somehow and then execute it?

Directly, I do not believe it is possible, this is not foreseen in the
syntax.

However, the <stdarg.h> / va_list mechanism achieves just this.
This mechanism is usually to achieve the reverse of what you are asking for
(dealing with a unknown number of arguments inside the function, in a
portable way).
But you could consider building yourself the va_list thingy, then call it.
Of course, the result will be highly tighted to a given compiler (or even a
given compiler for a given architecture.)


Antoine

Mike Caetano

unread,
May 25, 2005, 7:41:34 PM5/25/05
to
> printf("%d\n", average(3, 20, 30, 40));

The number of parameters here is determined at compile time, not run
time. It's the run time part that is tricky.

-Mike

John

unread,
May 26, 2005, 1:26:17 AM5/26/05
to

Oh, I misunderstood!

John

Mike Caetano

unread,
May 26, 2005, 4:44:27 AM5/26/05
to
>> time. It's the run time part that is tricky.
> Oh, I misunderstood!

It happens to all of us from time to time :)

To get back to the original question, the information at
http://www.drizzle.com/~scottb/gdc/fubi-paper.htm can be used to
accomplish the task on x86 machines running windows.

Here is a wrapper function from the above url that can be used to call
an arbitrary var_arg function.

DWORD Call_cdecl( const void* args, size_t sz, DWORD func )
{
DWORD rc; // here's our return value...
__asm
{
mov ecx, sz // get size of buffer
mov esi, args // get buffer
sub esp, ecx // allocate stack space
mov edi, esp // start of destination stack frame
shr ecx, 2 // make it dwords
rep movsd // copy params to real stack
call [func] // call the function
mov rc, eax // save the return value
add esp, sz // restore the stack pointer
}
return ( rc );
}

Notice that the args parameter is a pointer to run time arguments and
the sz paramter indicates how many bytes. This can lead to complications
because on an X86 under windows the stack alignment has to be
maintained. In English, that means that even though a parameter may be a
char or a short, it's pushed on the stack as a dword - 4 bytes. This
means taking special care when keeping track of the argument byte count.

How the arguments are packaged into an array is up to you, but it's
probably best to treat them as dwords regardless of what they really are.

Here is the above converted from intel syntax into lcc-win32 syntax and
with a few extra precautions thrown in. YMMV.

-Mike

If the formating is messed up copy and paste into wedit. That should
straighten it out.

#include <stdio.h>
#include <string.h>

unsigned long __declspec(naked) _stdcall Call_cdecl(
const void *args, // array containing arguments
unsigned long sz, // number of bytes of arguments
unsigned long funk // target function
)
{
_asm(
"pushl %ebp; \n" // save the frame pointer
"movl %esp,%ebp; \n" // establish new stack frame pointer
"pushl %edi; \n" // preserve previous value of edi
"pushl %esi; \n" // preserve previous value of esi
"pushl %ebx; \n" // preserve previous value of ebx
"movl 8(%ebp),%esi; \n" // args parameter into source register
"movl 12(%ebp),%ecx; \n" // sz parameter into counter register
"addl $3,%ecx; \n" // round up to next multiple of 4
"andl $0xFFFFFFFC,%ecx; \n" // per z = (x + d - 1) & ~(d-1);
"movl %ecx,%ebx; \n" // stash result in ebx
"subl %ecx,%esp; \n" // make space on stack
"movl %esp,%edi; \n" // stack pointer into dest register
"rep movsb; \n" // transfer args to stack -
destroys ecx
"call *16(%ebp); \n" // call target function
"addl %ebx,%esp; \n" // remove funk parameters from the stack
"popl %ebx; \n" // restore previous value of ebx
"popl %esi; \n" // restore previous value of esi
"popl %edi; \n" // restore previous value of edi
"leave; \n" // restore stack frame
"ret $12; \n" // return clearing parameters
);

}


void _stdcall VariableArgs(int length)
{
if ( length >= 24 ) {
printf("too many args\n");
return;
}

int result = 0;

unsigned long bytes = 0;

unsigned long args[24]; // 24 is just an arbitrary size

char format[128] = {0}; // seed the string or strcat won't work

// first arg to printf is a format specifier
args[0] = (unsigned long)format;

bytes = 4; // account for size of format ==> sizeof char*

for (int i=1; i < length; i++)
{
strcat(format, "%d ");
args[i] = (unsigned long)i;
bytes += sizeof(i); // 4;
}

strcat(format, "\n"); // add a new line

result = Call_cdecl(args, bytes, (unsigned long)printf);

printf("Call_cdecl returns %d\n", result);

}

int main(void)
{

for ( int i = 5; i < 25; i++)
{
VariableArgs(i);
}

return 0;

}

0 new messages