Wrapping a variadic function


B

BartC

I'm trying to wrap the standard function fprintf() with a version called
qfprintf(). The following was done according to an on-line tutorial, but it
doesn't work (prints three random numbers, not 10 20 30).

Should it work? If not, how can it be done?

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

int qfprintf(FILE* f, char* fmtstr, ...){
va_list args;
return vfprintf((FILE*)f, fmtstr, args);
}

int main(void) {

qfprintf(stdout,"%d %d %d\n", 10, 20, 30);

}
 
Ad

Advertisements

J

Joe Pfeiffer

BartC said:
I'm trying to wrap the standard function fprintf() with a version
called qfprintf(). The following was done according to an on-line
tutorial, but it doesn't work (prints three random numbers, not 10 20
30).

Should it work? If not, how can it be done?

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

int qfprintf(FILE* f, char* fmtstr, ...){
va_list args;
return vfprintf((FILE*)f, fmtstr, args);
}

int main(void) {

qfprintf(stdout,"%d %d %d\n", 10, 20, 30);

}

You've created the va_list you want to send to vfprintf(), but you
haven't hooked it up to the arguments you passed in:

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

int qfprintf(FILE* f, char* fmtstr, ...){
va_list args;

va_start(args, fmtstr); // *** new line -- this is your actual problem ***

return vfprintf(f, fmtstr, args); // *** why did you cast f to the type it already was? ***
}

int main(void) {

qfprintf(stdout,"%d %d %d\n", 10, 20, 30);

return 0; // *** don't forget your return value! ***
}
 
N

Noob

BartC said:
I'm trying to wrap the standard function fprintf() with a version called
qfprintf(). The following was done according to an on-line tutorial, but it
doesn't work (prints three random numbers, not 10 20 30).

Should it work? If not, how can it be done?

va_start and va_end are not optional.

See the EXAMPLE section:
http://man7.org/linux/man-pages/man3/stdarg.3.html

Random "on-line tutorials" are not the best source to learn C.
 
A

Alain Ketterlin

BartC said:
#include <stdio.h>
#include <stdarg.h>

int qfprintf(FILE* f, char* fmtstr, ...){
va_list args;
return vfprintf((FILE*)f, fmtstr, args);
}

You also need va_start(args,fmtstr) and va_end(args) around the call to
vfprintf.

-- Alain.
 
B

BartC

BartC said:
I'm trying to wrap the standard function fprintf() with a version called
qfprintf(). The following was done according to an on-line tutorial, but
it doesn't work (prints three random numbers, not 10 20 30).

Should it work? If not, how can it be done?
int qfprintf(FILE* f, char* fmtstr, ...){
va_list args;
return vfprintf((FILE*)f, fmtstr, args);
}

OK, thanks for the replies. It works now with the va_start() and va_end()
calls added (which were in the tutorial but I didn't notice them...).

(The reason for the extra cast which I forgot to edit out of my post - I
need to call fprintf() and similar from outside the source language of C,
and need to isolate those functions, and the special argument types such as
FILE* which are unknown outside of C.)
 
Ad

Advertisements

J

James Kuyper

On 03/25/2014 12:20 PM, Joe Pfeiffer wrote:
....
#include <stdio.h>
#include <stdarg.h>

int qfprintf(FILE* f, char* fmtstr, ...){
va_list args;

va_start(args, fmtstr); // *** new line -- this is your actual problem ***

return vfprintf(f, fmtstr, args); // *** why did you cast f to the type it already was? ***

That should be

int retval = vfprintf(f, fmtstr, args);
va_end(args);
return retval;

"... if the va_end macro is not invoked before the return, the behavior
is undefined." (7.16.1.3p2)
 
J

James Kuyper

OK, thanks for the replies. It works now with the va_start() and va_end()
calls added (which were in the tutorial but I didn't notice them...).

(The reason for the extra cast which I forgot to edit out of my post - I
need to call fprintf() and similar from outside the source language of C,
and need to isolate those functions, and the special argument types such as
FILE* which are unknown outside of C.)

In any context where FILE is unknown, it would be equally impossible to
either declare "FILE *f", or to use the (FILE*) cast. In any context
that is sufficiently C-like, knowing FILE well enough to allow the
declaration f as FILE* renders it unnecessary to cast f to (FILE*).
Note: in C, writing such code does not require that "FILE" be a complete
type - it could, for instance, be a forward-declared struct, with no
definition of the struct type itself.
Therefore, there must be additional differences between the language
you're actually using, and C, beyond the ones you've mentioned above, in
order to justify writing such code.
 
J

Joe Pfeiffer

James Kuyper said:
On 03/25/2014 12:20 PM, Joe Pfeiffer wrote:
...

That should be

int retval = vfprintf(f, fmtstr, args);
va_end(args);
return retval;

"... if the va_end macro is not invoked before the return, the behavior
is undefined." (7.16.1.3p2)

Ah, thank you. I didn't know that. I've got some code of my own to
fix!
 
Ad

Advertisements

M

Malcolm McLean

In any context where FILE is unknown, it would be equally impossible to
either declare "FILE *f", or to use the (FILE*) cast.
So obviously in the real code qfprintf() takes a void * or something else
which has been passed up to it from a function like qfopen(), and either
resolves to a FILE * or contains a FILE *. That wasn't really relevant to
the OP's question so he edited it out.
 
B

BartC

James Kuyper said:
On 03/25/2014 01:03 PM, BartC wrote:

In any context where FILE is unknown, it would be equally impossible to
either declare "FILE *f", or to use the (FILE*) cast. In any context
that is sufficiently C-like, knowing FILE well enough to allow the
declaration f as FILE* renders it unnecessary to cast f to (FILE*).
Note: in C, writing such code does not require that "FILE" be a complete
type - it could, for instance, be a forward-declared struct, with no
definition of the struct type itself.
Therefore, there must be additional differences between the language
you're actually using, and C, beyond the ones you've mentioned above, in
order to justify writing such code.

(Because a file handle is usually a FILE* type, ie. a pointer, then from
outside of C, any pointer to anything, or even any int of the right width,
will generally do. However, because C source code is still involved, it
causes problems.

The actual situation is a bit complex, but to try and explain, there are
three languages, let's call them:

A - An independent language
B - A hybrid which is a thin wrapper around C
C - Raw C itself

If A was translated to assembly or direct to native code, there wouldn't be
a problem - I would just need to know whether a FILE* argument was 32 bits
or 64-bits wide (on my likely range of platforms). The type mismatch with an
actual FILE* wouldn't get in the way as there is no C compiler involved.

But A at the moment generates C source code, and A knows nothing about a
FILE type, so it is hard for A to directly call fopen() for example. So some
wrapper functions are created, which I suppose could have been written in C,
but I used B which has access to the same headers. This wrapper takes
something like an int* type and casts it to FILE*. The wrapper is called
from A as a foreign function, with parameter types it understands.

The variadic functions that use FILE* are an extra detail. Although A has
its own arrangements for i/o, it's convenient to be able to call these from
A too (porting existing code using -printf() functions for example).)
 
B

BartC

Malcolm McLean said:
So obviously in the real code qfprintf() takes a void * or something else
which has been passed up to it from a function like qfopen(), and either
resolves to a FILE * or contains a FILE *. That wasn't really relevant to
the OP's question so he edited it out.

Actually, using a void* is a better idea than the int* I was using. And
obtaining such a handle from a wrapped fopen() is exactly how it works.
(Clearly I can't be the first person to try and make use of the C runtime
from an alternate language.)
 
J

James Kuyper

(Because a file handle is usually a FILE* type, ie. a pointer, then from
outside of C, any pointer to anything, or even any int of the right width,
will generally do. However, because C source code is still involved, it
causes problems.

The actual situation is a bit complex, but to try and explain, there are
three languages, let's call them:

A - An independent language
B - A hybrid which is a thin wrapper around C
C - Raw C itself

If A was translated to assembly or direct to native code, there wouldn't be
a problem - I would just need to know whether a FILE* argument was 32 bits
or 64-bits wide (on my likely range of platforms). The type mismatch with an
actual FILE* wouldn't get in the way as there is no C compiler involved.

But A at the moment generates C source code, and A knows nothing about a
FILE type, so it is hard for A to directly call fopen() for example. So some
wrapper functions are created, which I suppose could have been written in C,
but I used B which has access to the same headers. This wrapper takes
something like an int* type and casts it to FILE*. The wrapper is called
from A as a foreign function, with parameter types it understands.

The variadic functions that use FILE* are an extra detail. Although A has
its own arrangements for i/o, it's convenient to be able to call these from
A too (porting existing code using -printf() functions for example).)

After having added all that information, you still haven't justified the
code that you wrote, not even as something that was needed in another
context but which should have been removed before posting it in this
context.

In which of those languages (A, B, or C) is it both possible to declare
f as having the type "FILE*", and necessary, after having so declared
it, to convert it from "FILE*" to "FILE*"? What characteristic of that
language mandates such a no-op conversion?
 
B

BartC

James Kuyper said:
On 03/25/2014 04:06 PM, BartC wrote:
The actual situation is a bit complex, but to try and explain, there are
three languages, let's call them: [A,B,C]
After having added all that information, you still haven't justified the
code that you wrote, not even as something that was needed in another
context but which should have been removed before posting it in this
context.

In which of those languages (A, B, or C) is it both possible to declare
f as having the type "FILE*", and necessary, after having so declared
it, to convert it from "FILE*" to "FILE*"? What characteristic of that
language mandates such a no-op conversion?

The code snippet originated as 'B' and it was the C translator output that
was posted.

It started out as:

int qfprintf(int* f, char* fmtstr, ...){
va_list args;
return vfprintf((FILE*)f, fmtstr, args);

and should have been edited to:

int qfprintf(FILE* f, char* fmtstr, ...){
va_list args;
return vfprintf(f, fmtstr, args);

for the purpose of my varargs query. But the (FILE*) cast got left it by
mistake.
 
Ad

Advertisements

J

James Kuyper

James Kuyper said:
On 03/25/2014 04:06 PM, BartC wrote:
The actual situation is a bit complex, but to try and explain, there are
three languages, let's call them: [A,B,C]
After having added all that information, you still haven't justified the
code that you wrote, not even as something that was needed in another
context but which should have been removed before posting it in this
context.

In which of those languages (A, B, or C) is it both possible to declare
f as having the type "FILE*", and necessary, after having so declared
it, to convert it from "FILE*" to "FILE*"? What characteristic of that
language mandates such a no-op conversion?

The code snippet originated as 'B' and it was the C translator output that
was posted.

It started out as:

int qfprintf(int* f, char* fmtstr, ...){
va_list args;
return vfprintf((FILE*)f, fmtstr, args);

and should have been edited to:

int qfprintf(FILE* f, char* fmtstr, ...){
va_list args;
return vfprintf(f, fmtstr, args);

for the purpose of my varargs query. But the (FILE*) cast got left it by
mistake.

OK, now it makes sense. Note: you should have used void* for such
purposes, rather than int*. There are platforms where it would make a
difference, though if you don't believe you're using one of those
platforms, you're probably right.
 

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

Top