Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

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.

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().

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top