Calling a function when the number of parameters isn't known till runtime

J

John Friedland

[apologies if anyone thinks this is off-topic...]

My problem: I need to call (from C code) an arbitrary C library
function, but I don't know until runtime what the function name is,
how many parameters are required, and what the parameters are. I can
use dlopen/whatever to convert the function name into a pointer to
that function, but actually calling it, with the right number of
parameters, isn't easy.

As far as I can see, there are only two solutions:

1) This one is portable. If you know in advance that no function will
require more than, say, 2 parameters, then you can do something simple
like:

switch(nparams) {
case 0: (*user_func)(); break;
case 1: (*user_func)(P1); break;
case 2: (*user_func)(P1, P2); break;
}

2) If you don't know the maximum number of parameters, then there
seems to be no way to do this, short of writing assembler code. I
guess this would look something like this:

- the C code pushes all the parameters onto a local stack
- the C code calls an assembler routine, passing in the function
address, the local stack address, and maybe the number of parameters
- the assembler routine sets up a stack frame and does an indirect
call to the user's C code
- the assembler routine clears up the stack frame and returns

Any thoughts? Have I missed anything/does this make sense? If I have
to go for (2), I'll start with Linux/gcc/x86 and move on to other
systems if necessary. Does anyone know of any web resources that could
help me do this? I've got no idea how to do this at the moment.

Thanks -

John
 
M

Malcolm

John Friedland said:
[apologies if anyone thinks this is off-topic...]

My problem: I need to call (from C code) an arbitrary C library
function, but I don't know until runtime what the function name is,
how many parameters are required, and what the parameters are.
This is one of the few things that C won't let you do.
If you know the calling convention for your platform then with a spot of
assembly you can achieve this in minutes. However C provides no facilites
for building an argument list dynamically.

You might want to reconsider rewriting your design. For instance you can
pass in any number of parameters as a string.
 
M

MQ

John said:
[apologies if anyone thinks this is off-topic...]

My problem: I need to call (from C code) an arbitrary C library
function, but I don't know until runtime what the function name is,
how many parameters are required, and what the parameters are. I can
use dlopen/whatever to convert the function name into a pointer to
that function, but actually calling it, with the right number of
parameters, isn't easy.

Can you give us more context for the problem, because I'm not sure what
situation could make this problem arise.
 
R

rhyde

John said:
[apologies if anyone thinks this is off-topic...]

My problem: I need to call (from C code) an arbitrary C library
function, but I don't know until runtime what the function name is,
how many parameters are required, and what the parameters are. I can
use dlopen/whatever to convert the function name into a pointer to
that function, but actually calling it, with the right number of
parameters, isn't easy.

As far as I can see, there are only two solutions:

1) This one is portable. If you know in advance that no function will
require more than, say, 2 parameters, then you can do something simple
like:

switch(nparams) {
case 0: (*user_func)(); break;
case 1: (*user_func)(P1); break;
case 2: (*user_func)(P1, P2); break;
}

2) If you don't know the maximum number of parameters, then there
seems to be no way to do this, short of writing assembler code. I
guess this would look something like this:

- the C code pushes all the parameters onto a local stack
- the C code calls an assembler routine, passing in the function
address, the local stack address, and maybe the number of parameters
- the assembler routine sets up a stack frame and does an indirect
call to the user's C code
- the assembler routine clears up the stack frame and returns

Any thoughts? Have I missed anything/does this make sense? If I have
to go for (2), I'll start with Linux/gcc/x86 and move on to other
systems if necessary. Does anyone know of any web resources that could
help me do this? I've got no idea how to do this at the moment.


You are absolutely right. Short of using assembler code, or guessing
really high on the maximum number of parameters, there is no way to do
this. I'd opt for the assembly code. After all, I suspect you're going
to run into portability problems along the way, anyway.
Cheers,
Randy Hyde
 
S

Spiro Trikaliotis

Hello,

["Followup-To:" set to comp.lang.asm.x86]

John Friedland said:
My problem: I need to call (from C code) an arbitrary C library
function, but I don't know until runtime what the function name is,
how many parameters are required, and what the parameters are.
[...]

First of all: WHY do you want to do this? It does not sound like a clean
solution to me.
As far as I can see, there are only two solutions:
[...]

There might be a third solution, but it depends upon calling convention,
the machine you're looking at, and other things.

In "typical C calling conventions", the parameters are put on the stack
in reverse order often.

That is, a call

f(p1, p2, p3)

aften looks something like

PUSH p3
PUSH p2
PUSH p1
call f
ADD SP,12 ; clean up stack after call


while a call like f(p1) looks something like

PUSH p1
call f
ADD SP,4

You see that there these look very similar. Thus, if you know an upper
limit for the number of parameters (for example, 6), you might have
success with calling every function as if it had 6 parameters. The
additional parameters will simply be ignored by the called function.
Note that this will NOT work with other calling conventions, if the
called function is reponsible for cleaning up the stack, or if the order
of pushed parameters is not reversed. Also, to come back to the topic of
clc, this might not work on machines which do not use a stack at all. ;)

Of course, there are other subtlelities which might give you problems
(differences between long and int, pointer and int, and so on, or other
calling conventions). Anyway, as all of this is way beyond the scope of
C, so I set the followup to clax86.

Any thoughts? Have I missed anything/does this make sense?

Yes, you totally forgot that there might be different types of
parameters. Anyway, I neglected these details, too. ;)

Regards,
Spiro.
 
K

Kenny McCormack

You are absolutely right. Short of using assembler code, or guessing
really high on the maximum number of parameters, there is no way to do
this. I'd opt for the assembly code. After all, I suspect you're going
to run into portability problems along the way, anyway.

(OT, blah, blah, blah)

Look into a package called: avcall
Reasonably portable - works on all the major platforms.
 
A

av

switch(nparams) {
case 0: (*user_func)(); break;
case 1: (*user_func)(P1); break;
case 2: (*user_func)(P1, P2); break;
}

the solution seems easy but i have not try to compile and
you have to change the function add a dummy arg "int n"

int u_f0(int n, ... );
int u_f1(int n, ... );
int u_f2(int n, ... );
int u_f3(int n, ... );
int u_f4(int n, ... );
int u_f5(int n, ... );
int u_f6(int n, ... );
int u_f7(int n, ... );
etc

int (*u_fu)(int, ...);
you has to use it in this way


u_fu=u_f0; u_fu(0);
u_fu=u_f1; u_fu(1, p1);
u_fu=u_f2; u_fu(2, p1, p2);
u_fu=u_f3; u_fu(3, p1, p2, p3);

Is it possible this is the only "active" thread that i see in
comp.lang.c? it seems my news server has no more comp.lang.c
 
A

,a\\/b

-----------------------
; nasmw -fobj thisfile.asm
; bcc32 -v file.c thisfile.obj

section _DATA public align=4 class=DATA use32

extern _array
global _ftot

where_return dd 0, 0
what_func dd 0, 0

section _TEXT public align=1 class=CODE use32


_ftot:
pop dword [where_return]
pop dword [what_func]
mov eax, [what_func]
call dword [ _array + 4*eax]
push dword [what_func]
push dword [where_return]
ret
-----------------------------------------

#include <stdio.h>

int f0(){printf("sono f0\n"); return 0;}
int f1(int i){printf("sono f1\n"); return i;}
int f2(int i, int j){printf("sono f2\n"); return i+j;}
int f3(int i, int j, int h)
{printf("sono f3\n"); return i+j+h;}

char *array[4]={(char*)&f0, (char*)&f1, (char*)&f2, (char*)&f3};

int ftot(int i, ...);

int main(void)
{int i, j, k;

printf("quanti parametri scegliere> ");
i=scanf("%i", &j);
if(i!=1) {printf("Errore"); return 0;}
switch(j){
case 0: k=ftot(0); break;
case 1: k=ftot(1, 1); break;
case 2: k=ftot(2, 1, 1); break;
case 3: k=ftot(3, 1, 1, 1); break;
default: k=ftot(0); break;
}
printf("k==%i\n", k);
return 0;
}
 
G

Guest

[apologies if anyone thinks this is off-topic...]

My problem: I need to call (from C code) an arbitrary C library
function, but I don't know until runtime what the function name is,
how many parameters are required, and what the parameters are. I can
use dlopen/whatever to convert the function name into a pointer to
that function, but actually calling it, with the right number of
parameters, isn't easy.

Unless performance is a real issue, is this really a problem? If
you allow for the maximum number of parameters (15? 20?) just set
up your code for that, i.e.

(*user_func)(P1, P2, P3, .... P20);

Any parameters stacked over and above what user_func() expects
will be ignored, but importantly the caller will clean up the
stack afterwards. This would be very inefficient if most of the
functions called expected only one or two parameters, but should
still work and remain portable. Unless I'm missing something, of
course (quite possible!).

Pete
 
R

randyhyde

Kenny said:
(OT, blah, blah, blah)

Look into a package called: avcall
Reasonably portable - works on all the major platforms.

Something about the phrase: "There are typically built-in limits on the
size of the argument-list, which may also include the size of any
structure arguments. " tells me that this package doesn't *exactly*
provide what the OP was asking for. The package looks cool, but it
does have limitations, not unlike those of the "switch" approach the OP
was using. Definitely more convenient, but...

And correct me if I'm wrong, but when I'd heard about this package, I
was under the impression that it was a bunch of macros that emitted
in-line assembly code to achieve the desired results. Portability was
achieved by having a lot of different versions (for different object
file formats and processors). Maybe I misunderstood how the package
actually worked? But it sounded to me like they were using assembly
language when I briefly looked at this package.
Cheers,
Randy Hyde
 
K

Kenny McCormack

Something about the phrase: "There are typically built-in limits on the
size of the argument-list, which may also include the size of any
structure arguments. " tells me that this package doesn't *exactly*
provide what the OP was asking for. The package looks cool, but it
does have limitations, not unlike those of the "switch" approach the OP
was using. Definitely more convenient, but...

Everything has limitations. And, for all I know, this "libfii" or
whatever it was that someone referred to somewhere in this thread, is
better. It sounded to me like this (libfii or whatever) is what avcall
evolved into.

But I've been using avcall for years now and have never had any reason
to complain. It has done everything it claims, and on several different
platforms.
And correct me if I'm wrong, but when I'd heard about this package, I
was under the impression that it was a bunch of macros that emitted
in-line assembly code to achieve the desired results. Portability was
achieved by having a lot of different versions (for different object
file formats and processors). Maybe I misunderstood how the package
actually worked? But it sounded to me like they were using assembly
language when I briefly looked at this package.

All true. And I suppose that if "assembly language" is a dirty word (as
it is in the dominant religion of this ng), then you'll have to live
with it. But I got news for you (and please, don't shoot me for being
the messenger), but (oh man, it hurts to even think this), your compiler
almost certainly generates assembly code and (gasp!) might very well emit
machine code (*). Oh, the horror!

(*) Pedant's note: If not the compiler, then some other piece of your
toolchain.
 
N

Nicholas Howe

John Friedland said:
1) This one is portable. If you know in advance that no function will
require more than, say, 2 parameters, then you can do something simple
like:

switch(nparams) {
case 0: (*user_func)(); break;
case 1: (*user_func)(P1); break;
case 2: (*user_func)(P1, P2); break;
}

Another portable C solution is to require that functions pointed to by
user_func take a single parameter. Instead of directly invoking arbitrary
functions through your user_func pointer you or whoever wishes his arbitrary
function to be called provides a parameter struct and proxy function for the
arbitrary function. The proxy function interprets its parameter as a
pointer to a parameter struct containing parameters for the arbitrary
function that it represents. This requires defining a parameter struct and
proxy function for each arbitrary function, but only as the need to call the
arbitrary function through your user_func pointer arises.
2) If you don't know the maximum number of parameters, then there
seems to be no way to do this, short of writing assembler code. I
guess this would look something like this:

- the C code pushes all the parameters onto a local stack
- the C code calls an assembler routine, passing in the function
address, the local stack address, and maybe the number of parameters
- the assembler routine sets up a stack frame and does an indirect
call to the user's C code
- the assembler routine clears up the stack frame and returns

This description doesn't make sense to me. If you are in a position to
place parameters on a local stack and then immediately invoke your arbitrary
function, thereby implying that you know the types and number of parameters
at the point-of-call, then you can call directly through user_func using the
normal syntax for a function call through a function pointer whether you are
programming in C or assembly language. The problem I have attempted to
solve above is to provide a mechanism for parameters to be passed from one
section of code through an abstract section that is unaware of what function
it is calling and what parameters it is passing to a third section that
expects specific parameters of different types and number. This is only
necessary if the parameter list's size in bytes is unknown at the
point-of-call, in which case it cannot be copied onto a local stack.

Nicholas Howe
 

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

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top