Linux has this nice function dprintf
:
The functions
dprintf()
andvdprintf()
(as found in the glibc2 library) are exact analogues offprintf()
andvfprintf()
, 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 ofprintf()
, 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()
(orDPRINTF
) is also a popular macro name for a debuggingprintf
. So, probably, it is better to avoid this function in programs intended to be portable.
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?
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With