Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cross-platform compatibility of dprintf

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?

like image 459
BCS Avatar asked Jan 21 '10 22:01

BCS


2 Answers

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.

like image 183
Alok Singhal Avatar answered Oct 26 '22 18:10

Alok Singhal


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.

like image 24
ephemient Avatar answered Oct 26 '22 18:10

ephemient