tags:

views:

314

answers:

4

Linux has this nice function dprintf:

The functions dprintf() and vdprintf() (as found in the glibc2 library) are exact analogues of fprintf() and vfprintf(), except that they output to a file descriptor fd instead of to a given stream.

however as that same source points out:

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.

My question

How can I make a setup that will safely call the dprintf that I want, if it exists, or fail to compile with some reasonably sane error message if that function doesn't exist? I guess I'd do something like this:

#ifdef SOMETHING
    #define Dprintf dprintf
#else
    #error "no dprintf"
#endif

but I don't know what SOMETHING should be. I guess I could restrict it to just Linux but could I make it looser?

A: 

In conjunction to dmcer's answer, here's a link to a similar question found here.

Hope this helps, Best regards, Tom.

tommieb75
+2  A: 

An Autoconf test could check whether dprintf(1, "blablabla") writes to stdout or stderr, and whether dprintf(-1, "blablabla") fails in an interesting way or writes to stderr.

If libc gives you with the wrong dprintf(), you could implement it yourself on top of snprintf() (or even better asprintf()) and write(). And then mention your 'dprintf.o' to the linker so it prefers yours to libc's.

If you want a better name, I propose fdprintf().

sverkerw
+1 (even thought I'm not using autoconf, or any configuration for that matter) -- I'm using dprintf in place of asprintf+write because it's used in an error handler and I don't want to have to worry about the heap working.
BCS
@BCS: You don't need to use the heap, your function can allocate the buffer on the stack (either a fixed size buffer, or with `alloca()`)
Hasturkun
@Hasturkun: Fixed size won't work (I don't have a reasonable upper bound on length) and under some error recovery situations, stack space is very tight.
BCS
Implementing it on top of `snprintf` or `asprintf` is a bad idea, as these require the string to fit in memory and have failure cases. Instead, use `fdopen` and then just use `fprintf`.
R..
@R: To quote Mr. Simpson: "Duh!".
sverkerw
+2  A: 

You could make an autoconf test, if you're using autotools for your buildsystem.

AC_LANG([C])
AC_USE_SYSTEM_EXTENSIONS
AC_ARG_WITH([dprintf],
  [AS_HELP_STRING([--with-dprintf],
    [Assume that dprintf prints to a specified file descriptor])],
  [], [with_dprintf=check])
AS_IF([test "x$with_dprintf" = xcheck],
  [AC_RUN_IFELSE(
    [AC_LANG_PROGRAM([[
        #include <stdio.h>
        #include <string.h>
        int debug_level;
      ]], [[
        char msg[] = "Hello, world!\n";
        char rcv[sizeof(msg)] = "";
        FILE *f = tmpfile();
        int fd = fileno(f);
        debug_level = fd - 1;
        dprintf(fd, "%s", msg);
        fseek(f, 0, SEEK_SET);
        fread(rcv, 1, sizeof(msg), f);
        return strcmp(msg, rcv);
      ]]
    )], [with_dprintf=yes])])
AS_IF([test "x$with_dprintf" = xyes],
  [AC_DEFINE([HAVE_DPRINTF], [1],
    [dprintf prints to a specified file descriptor])])

(Allowing for --with-dprintf or --without-dprintf when cross-compiling, as AC_RUN_IFELSE isn't valid in those cases.)


fdopen isn't in standard C, but it is in POSIX.2 and later. I don't recall any UNIX-like that doesn't have it -- heck, even Windows has it.

int fdprintf(int fd, char *fmt, ...) {
    va_list ap;
    FILE *f = fdopen(fd);
    int rc;

    va_start(ap, &fmt);
    rc = vfprintf(f, fmt, ap);
    fclose(f);
    va_end(ap);
    return rc;
}

Of course, if you know which file descriptor you'll be printing to, just keep the FILE* pointer around instead.

ephemient
I don't have a build system (OK, I'm using make via the make-remaking-make files thing)
BCS
+3  A: 

Looks like dprintf() is actually in POSIX.1-2008 (with the semantics you want), so you can do this:

#if !defined(__GLIBC__) && _POSIX_C_SOURCE < 200809
#error "dprintf may not exist, or may be wrong"
#endif

__GLIBC__ is defined if you're using a gnu build system. This is probably as safe as you can be without writing a small test program.

Alok
BTW, it is frequent to see things like `(_POSIX_C_SOURCE - 0) < 200809` so that it's still a valid (false) expression even when the macro is undefined or empty.
ephemient
But if something isn't defined, the pre-processor will substitute 0 for it, so `_POSIX_C_SOURCE < 200809` will expand to `0 < 200809` if `_POSIX_C_SOURCE` is not defined or is 0. For empty macros, you're right.
Alok