tags:

views:

207

answers:

5

Hi,

In C/C++, there is a 'write() function which let me write to either file or a socket, I just pass in the file descriptor accordingly). And there is a fprintf() which allow me to do fprintf (myFile, "hello %d", name); but it only works for file.

Is there any api which allows me to do both? i.e. able to let me do print formatting and able to switch between writing to file or socket?

Thank you.

+4  A: 

You can use sprintf or snprintf to print to a char * buffer, and then use write. To get a file descriptor from a FILE * variable, you can use fileno. There is no portable way to go from a file descriptor to a FILE *, though: you can portably to use fdopen to associate a FILE * with a valid file descriptor.

In addition, the latest POSIX standard specifies dprintf, but the GNU libc dprintf man page has this to say:

These functions are GNU extensions, not in C or POSIX. Clearly, the names were badly chosen. Many systems (like MacOS) have incompatible functions called dprintf(), usually some debugging version of printf(), perhaps with a prototype like

void dprintf (int level, const char *format, ...);

where the first parameter is a debugging level (and output is to stderr). Moreover, dprintf() (or DPRINTF) is also a popular macro name for a debugging printf. So, probably, it is better to avoid this function in programs intended to be portable.

Of course, the libc manual page is not updated with the latest standard in mind, but you still have to be careful with using dprintf, since you might get something you don't want. :-)

Alok
Problem with `snprintf` (I'll pretend you didn't mention `sprintf`) is that it's tricky to figure out the size of the buffer without making every `my_printf` potentially do a `malloc` and `free`. Unless you know a better way than 1) try `snprintf` with an arbitrary-size buffer on the stack, 2) if `snprintf` returned a number smaller than the buffer, then `write` the buffer and return, else 3) `malloc` a buffer at least that size, `snprintf` into it, `write` it, then `free` it and return.
Mike DeSimone
@Mike: `snprintf()` with `NULL` buffer and 0 size gives you the size required in C99. But that means `snprintf` is called twice for each buffer. I like `dprintf` the most, but it may not be available. `fdopen` is nice, but I am fairly sure that one has to create two `FILE *` if one wants to read and write using that approach. Thanks for your comment!
Alok
It's interesting that they say MacOS has an incompatible `dprintf`, because `man dprintf` on my Mac OS X 10.6 box gives `No manual entry for dprintf`. I had never heard of `dprintf` before, anyway.
Mike DeSimone
@Mike: Yeah, I just noticed that too, so maybe it was in earlier MacOS versions.
Alok
+4  A: 

Sure: just use fdopen on the socket to create a FILE* stream, and use fprintf appropriately.

Chris Jester-Young
on GNU systems, there's also `dprintf()` (see eg http://linux.die.net/man/3/dprintf )
Christoph
One issue about using fdopen is that you must use fclose (or you may leak resources) and you cannot use close (or you will double close the descriptor). If your code is not responsible for the descriptor lifetime, you shouldn't use fdopen.
R Samuel Klatchko
+5  A: 

In C, on POSIX-ish machines (which you must have to be using 'write()'), you can use:

  • fdopen() to create a file stream from a file descriptor.
  • fileno() to obtain a file descriptor from a file stream.

You need to be careful about flushing the file stream at appropriate times before using the matching file descriptor.

Jonathan Leffler
One issue about using fdopen is that you must use fclose (or you may leak resources) and you cannot use close (or you will double close the descriptor). If your code is not responsible for the descriptor lifetime, you shouldn't use fdopen.
R Samuel Klatchko
A: 

You can do something like this, maybe :

#include <stdarg.h>

int fdprintf(int fd, const char* fmt, ...) {
  char buffer[4096] = {0};
  int cc;
  va_list args;
  va_start(args, fmt);
  if ((cc = vsnprintf(buffer, 4096, fmt, args)) > 0) {
    write(fd, buffer, cc);
  }
  va_end(args);
  return cc;
}
Bertrand Marron
this won't work if the resulting string is more than 4096 bytes. you can determine the needed buffer size by int len =_vscprintf(fmt,args)+1; in Visual C++
uray
@uray: with a standard compliant vsnprintf(), you can use: `len = vsnprintf(0, 0, fmt, args) + 1;`
Jonathan Leffler
msdn says http://msdn.microsoft.com/en-us/library/1kt27hek%28VS.80%29.aspx ... "If buffer or format is NULL, or if count is less than or equal to zero, these functions invoke the invalid parameter handler, as described in Parameter Validation. If execution is allowed to continue, these functions return -1 and set errno to EINVAL" does it comply to standard?
uray
A: 

generalizing tusbar answer, and to make it work with visual studio you can try the following codes: `

int fdprintf(int fd, const char* fmt, ...) {
    int cc;
    va_list args;
    va_start(args, fmt);
    int len   = _vscprintf(fmt,args) + 1;
    char* buffer = new char[len];
    buffer[len] = 0;
    if ((cc = vsprintf_s(buffer, len-1, fmt, args)) > 0) {
        write(fd, buffer, cc);
    }
    va_end(args);
    delete[] buffer;
    return cc;
}

`

uray