Or you might like this variation which supports any number of arguments and return values
int c_call_fz(int nargs) {
char ftn[51];
if(nargs-- == 0)
cfgl_fatal("usage: c_call_fz([args, ...] ftnname char(*)) [returning values...]");
poptrim(ftn, sizeof(ftn));
return fglcapi_call4gl(ftn, nargs);
} /* c_call_fz */
poptrim() is just a little function of mine which pops a char string and blank-trims it, no trouble for anyone to write that.
cfgl_fatal() is a function I use for uniform reporting of failure to call a C function properly, followed by program termination (programmers better deal with the fault Codd-damn it or they ain't getting a running program!)
You'll notice that the name of the function is at the end (reflected in my mnemonic _fz wart at the end of the function name). I did this because getting it from the bottom of the stack is too much hard work. I had a version that would do that, but it could not cope with data types like datetimes, etc. So I decided to put the function name at the end, and indicate it's a dodgy interim version of the function by naming it with the _fz wart. One day a first-class c_call might be possible...
In your fglext.c, you just need
{ "c_call_fz", c_call_fz, -1, -1 },
to make fgl2p happy compiling this function with variable arg and return lists, you need to use the -z flag and a file that lists this function:
fgl2p -z /somewhere/varargftns.lst thingy.4gl
where my /somewhere/varargftns.lst contains something like
c_call_fz -1 -1
amongst others
given all the conditions mentioned above, the same .4gl file can call
call c_call_fz("ftn1")
call c_call_fz(1, 2, "ftn2") returning a, b, c
and so on without getting compile time errors.