va_list + function pointers

L

Lurkos

Hello.

I'm trying to figure out how can I make use of stdarg.h/va_list with
function pointers as variable arguments.

In particular I don't know which type I should put in va_arg call and
which name I should put in va_start call.

Do you know how to implement it in C?

Thanks!

e.g. (just tentative)
double myFunc(int k, double x, double (*fp1)(double,int), ...)
{
double result;
va_list vl;

va_start(vl, ????????);

do {
double (*fp_new)(double,int) = va_arg(vl, ???????);

// do stuff

} while ( (*fp_new) != NULL );

va_end(vl);

return result;
}
 
B

Ben Pfaff

Lurkos said:
I'm trying to figure out how can I make use of stdarg.h/va_list with
function pointers as variable arguments.

In particular I don't know which type I should put in va_arg call and
which name I should put in va_start call.

It's not any different from any other varargs usage. Put the
final parameter name in va_start and the correct type in va_arg.
 
J

James Kuyper

Hello.

I'm trying to figure out how can I make use of stdarg.h/va_list with
function pointers as variable arguments.

In particular I don't know which type I should put in va_arg call and

The type of the object into which you are storing the result of that call.
which name I should put in va_start call.

The name of the last named argument in the function declaration.
Do you know how to implement it in C?

Thanks!

e.g. (just tentative)
double myFunc(int k, double x, double (*fp1)(double,int), ...)
{
double result;
va_list vl;

va_start(vl, ????????);

va_start(vl, fp1)
do {
double (*fp_new)(double,int) = va_arg(vl, ???????);

va_arg(vl, double(*)(double,int))
// do stuff

} while ( (*fp_new) != NULL );

I strongly suspect you mean while(fp_new != NULL), which can be
simplified to

} while(fp_new);

In the unlikely event that you did intend to dereference fp_new in that
expression, you need to understand that it can only be done as part of a
function call; and given the type of the function, you need to provide
two arguments, too. So you could write:

(*fp_new)(3.0, 2) != NULL

Due to special rules for function pointers, that can be simplified to

fp_new(3.0, 2) != NULL

However, since fp_new returns a double, and NULL could be (void*)0,
that's not a reasonable thing to do; it could violate a constraint,
which is one reason why I'm pretty sure it's not what you intended to do.
 
B

Ben Bacarisse

Lurkos said:
I'm trying to figure out how can I make use of stdarg.h/va_list with
function pointers as variable arguments.

In particular I don't know which type I should put in va_arg call and
which name I should put in va_start call.

As the other Ben said,nothing special here. If you don't know how to
use va_arg and friends for "ordinary" argument types, post a simpler
example first. You can then move to function pointers with no change.
Do you know how to implement it in C?

Thanks!

e.g. (just tentative)
double myFunc(int k, double x, double (*fp1)(double,int), ...)

This looks odd. fp1 is unused and the ... seems to refer to list of
function pointers of the same type as fp1. If there must always be one
function pointer (hence your declaration of one) then you should use it
in the example code. If there can be zero or more function pointers,
then you should stop at "double x, ...".
{
double result;
va_list vl;

va_start(vl, ????????);

You always put the last declared parameter here: fp1 in this case.
do {
double (*fp_new)(double,int) = va_arg(vl, ???????);

You must put the type of the argument that the caller will pass here.
Presumably double (*)(double, int). This rule can be relaxed a little
bit, but I see no reason to make use of that permission here.
// do stuff

} while ( (*fp_new) != NULL );

*fp_new is an expression of function type. You can't test it like this.
You can call the function *fp_new or you can test if fp_new == NULL.
 
B

Ben Bacarisse

James Kuyper said:
The type of the object into which you are storing the result of that
call.

That's a little too restrictive -- more so even than my rather too
restrictive reply. The type must be compatible with the type of the
argument (after promotion) and can therefore be quite different from the
type into which your storing the result.

What's more, the type must be such that a pointer type can be formed by
appending a * (e.g. int => int *, char * => char **). So in the OP's
case, a typedef is needed, I think.

<snip>
 
B

Ben Bacarisse

Sorry about replying to myself but I must make a correction.

Ben Bacarisse said:
As the other Ben said,nothing special here. If you don't know how to
use va_arg and friends for "ordinary" argument types, post a simpler
example first. You can then move to function pointers with no change.

That's not quote true. There is something special here. The type must
be compatible with (take that to mean "the same as" for now) the type of
the supplied argument, but it must be in a form that enable a pointer
type to be derived form it by simply appending a *. See below for
details.
This looks odd. fp1 is unused and the ... seems to refer to list of
function pointers of the same type as fp1. If there must always be one
function pointer (hence your declaration of one) then you should use it
in the example code. If there can be zero or more function pointers,
then you should stop at "double x, ...".


You always put the last declared parameter here: fp1 in this case.


You must put the type of the argument that the caller will pass here.
Presumably double (*)(double, int). This rule can be relaxed a little
bit, but I see no reason to make use of that permission here.

The trouble is that a pointer to a pointer of that type is written
double (**)(double, int) so my answer fails to meet the extra
requirement in 7.16.1.1 p2. It worked when I tried which is what
stopped me from checking. I think you need a typedef here to make it
conform:

typedef double (*func_ptr)(double, int);
...
func_ptr fp_new = var_arg(vl, func_ptr);
*fp_new is an expression of function type. You can't test it like this.
You can call the function *fp_new or you can test if fp_new == NULL.

Since I'm here, note that, technically, passing NULL in a call to myFunc
is not correct. It's type, after promotion, is not compatible with the
type you intend to give to va_arg. This you must end the argument list
with (func_ptr)NULL (or (double (*)(double, int))NULL if the typedef is
not visible here).

As it happens, the same problem occurs with ordinary pointers when
passing them to vararg functions. NULL could simply be 0, and 0
promotes to the integer 0 when being passed to a vararg function in the
.... position. To pass a null pointer you need to write (void *)0.

You can test for NULL in your function, but you can never rely on
passing NULL as a null pointer to a vararg function.
 
B

Ben Pfaff

Ben Bacarisse said:
That's not quote true. There is something special here. The type must
be compatible with (take that to mean "the same as" for now) the type of
the supplied argument, but it must be in a form that enable a pointer
type to be derived form it by simply appending a *. See below for
details.

Ah, I forgot about that detail in my reply, even though I've run
into it myself more than once. Oops.
 
J

James Kuyper

That's a little too restrictive -- more so even than my rather too
restrictive reply. The type must be compatible with the type of the
argument (after promotion) and can therefore be quite different from the
type into which your storing the result.

I thought that "compatible" and "promotion" were complications that the
OP was probably not ready to deal with yet. However, I should have
emphasized that it's the type of the argument that matters, not the
place where you store it. Of course, for function pointers it will
seldom make sense to store it anything other than an object of the
original type.
What's more, the type must be such that a pointer type can be formed by
appending a * (e.g. int => int *, char * => char **). So in the OP's
case, a typedef is needed, I think.

Yes, I'd forgotten about that issue; it makes the OP's question less
trivial than I thought it was.
 
B

Ben Bacarisse

Ben Pfaff said:
Ah, I forgot about that detail in my reply, even though I've run
into it myself more than once. Oops.

And you managed to get though the exceptionally high density of typos
(even for me)!
 
B

Ben Bacarisse

James Kuyper said:
I thought that "compatible" and "promotion" were complications that the
OP was probably not ready to deal with yet. However, I should have
emphasized that it's the type of the argument that matters, not the
place where you store it.

Yes, but I was really going for that second point -- it's the argument
type that matters.

int as_int = va_arg(vl, int);

is wrong if the supplied arguments are double.
Of course, for function pointers it will
seldom make sense to store it anything other than an object of the
original type.

Yes, but that's also a somewhat subtle point that the OP may not yet
know. If the OP took your remark as suggesting the va_arg "magic" can
convert the function pointer so that it's suitable for storage and,
maybe by extension, for use as some other type, there would be trouble.
Yes, I'd forgotten about that issue; it makes the OP's question less
trivial than I thought it was.

That makes three of us!
 
L

Lurkos

*Ben Bacarisse* said:
This looks odd.  fp1 is unused and the ... seems to refer to list of
function pointers of the same type as fp1.  If there must always be one
function pointer (hence your declaration of one) then you should use it
in the example code.  If there can be zero or more function pointers,
then you should stop at "double x, ...".

You're right. I was not completely clear.

The problem I have to solve is to compute a Taylor series.
The function pointer fp1 enables me to use the same function to
evaluate the series using different actual functions to compute the
Taylor coefficient.

The problem arises when I have a multiplication between two different
Taylor series.
In this case a single function pointer is not enough, because I need
to compute the Cauchy product of the two Taylor coefficients.

I though it could be solved in C making the first function pointer
mandatory, and the second optional.
To implement the "optional" second parameter, I though about using
va_list, and checking for a NULL pointer to recognize the end of the
optional arguments.

Of course, both the two function pointers must have the same input and
output type.

Therefore I expect to have this two possible function call:
myFunc(k,x,fp1,NULL) and myFunc(k,x,fp1,fp2,NULL).
You always put the last declared parameter here: fp1 in this case.
OK.


You must put the type of the argument that the caller will pass here.
Presumably double (*)(double, int).  This rule can be relaxed a little
bit, but I see no reason to make use of that permission here.

Thanks.
I was not sure it would be allowed in C to use a statement like
"double(*)".
This, of course, solves the problem.
*fp_new is an expression of function type.  You can't test it like this..
You can call the function *fp_new or you can test if fp_new == NULL.

Right. I need to check the pointer itself.
 
E

Eric Sosman

[...]
I though it could be solved in C making the first function pointer
mandatory, and the second optional.
To implement the "optional" second parameter, I though about using
va_list, and checking for a NULL pointer to recognize the end of the
optional arguments.

Of course, both the two function pointers must have the same input and
output type.

Therefore I expect to have this two possible function call:
myFunc(k,x,fp1,NULL) and myFunc(k,x,fp1,fp2,NULL).

You mean

myFunc(k, x, fp1, (double(*)(double,int))NULL)
and
myFunc(k, x, fp1, fp2, (double(*)(double,int))NULL)

It's important to specify the proper type, because pointers of
different types may have different representations. In the
case of NULL it's particularly important because the macro
NULL may expand to an unadorned 0, which will look like an int
and not like any kind of pointer at all. Usually the compiler
can figure out from context that the 0 is to be taken as a null
pointer value, but the compiler has no knowledge of the types
of arguments that match the `...' portion of a parameter list.

A typedef could make this less cumbersome. One way:

typedef double (*Fptr)(int, double);
...
myFunc(k, x fp1, (Fptr)NULL);

Another way:

typedef double Func(int, double);
...
myFunc(i, x, fp1, (Func*)NULL);

Either typedef might also make myFunc() a little easier to write.
 
B

Ben Bacarisse

Lurkos said:
You're right. I was not completely clear.

The problem I have to solve is to compute a Taylor series.
The function pointer fp1 enables me to use the same function to
evaluate the series using different actual functions to compute the
Taylor coefficient.

You might find a language in which you can construct function at run
time (so call first-class functions) mire useful than C here. But there
may be other reasons you've decided on C...
The problem arises when I have a multiplication between two different
Taylor series.
In this case a single function pointer is not enough, because I need
to compute the Cauchy product of the two Taylor coefficients.

I though it could be solved in C making the first function pointer
mandatory, and the second optional.
To implement the "optional" second parameter, I though about using
va_list, and checking for a NULL pointer to recognize the end of the
optional arguments.

Of course, both the two function pointers must have the same input and
output type.

Therefore I expect to have this two possible function call:
myFunc(k,x,fp1,NULL) and myFunc(k,x,fp1,fp2,NULL).

There is no need for variable arguments in this case. There are only
two calls: one with two function pointers and one with a null pointer
in place of the second.

Also, with varags, you can't safely pass NULL. You must pass a null
function pointer such as (double (*)(double, int))0 but if you have a
normal function with two function pointer parameters, NULL will be
converted to the right type for the call. It all works out much
simpler.

<snip>
 
L

Lurkos

You might find a language in which you can construct function at run
time (so call first-class functions) mire useful than C here.  But there
may be other reasons you've decided on C...

Right.
Actually I've decided on C because I need to make use of MPFR library
and of course because I would like to practice more with C and its low
level (and much interesting) features.
There is no need for variable arguments in this case.  There are only
two calls: one with two function pointers and one with a null pointer
in place of the second.

Yes, it would be simpler indeed.
Also, with varags, you can't safely pass NULL.  You must pass a null
function pointer such as (double (*)(double, int))0 but if you have a
normal function with two function pointer parameters, NULL will be
converted to the right type for the call.  It all works out much
simpler.

I wasn't aware of that problem.
Thank you (and all the other) that pointed it out and clarified it to
me.
 

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,770
Messages
2,569,583
Members
45,073
Latest member
DarinCeden

Latest Threads

Top