Кроссплатформенная совместимость dprintf - PullRequest
5 голосов
/ 22 января 2010

В Linux есть эта приятная функция dprintf:

Функции dprintf() и vdprintf() (как в библиотеке glibc2) являются точными аналогами fprintf() и vfprintf(), за исключением того, что они выводят в файловый дескриптор fd вместо заданного потока.

однако, как указывает тот же источник:

Эти функции являются расширениями GNU, а не в C или POSIX. Понятно, что имена были выбраны плохо. Многие системы (например, MacOS) имеют несовместимые функции, называемые dprintf(), обычно это отладочная версия printf(), возможно, с прототипом, подобным

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

где первый параметр - уровень отладки (и вывод на stderr). Более того, dprintf() (или DPRINTF) также является популярным именем макроса для отладки printf. Так что, вероятно, лучше избегать этой функции в программах, предназначенных для переносимости.

Мой вопрос

Как я могу выполнить настройку, которая будет безопасно вызывать dprintf, который я хочу, если он существует, или не сможет скомпилировать какое-то достаточно разумное сообщение об ошибке, если эта функция не существует? Я думаю, я бы сделал что-то вроде этого:

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

но я не знаю, каким должно быть SOMETHING. Я думаю, я мог бы ограничить это только Linux, но я мог бы сделать его более свободным?

Ответы [ 4 ]

9 голосов
/ 22 января 2010

Похоже, dprintf() на самом деле в POSIX.1-2008 (с необходимой вам семантикой), так что вы можете сделать это:

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

__GLIBC__ определяется, если вы используете систему сборки GNU. Это, вероятно, настолько безопасно, насколько это возможно без написания небольшой тестовой программы.

4 голосов
/ 22 января 2010

Вы можете сделать тест autoconf, если вы используете autotools для вашей системы сборки.

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])])

(с учетом --with-dprintf или --without-dprintf при кросс-компиляции, поскольку AC_RUN_IFELSE в этих случаях недопустимо.)


fdopen не соответствует стандарту C, но равно в POSIX.2 и более поздних версиях. Я не помню ни одного UNIX-подобного, в котором бы его не было - черт, даже в Windows это есть.

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;
}

Конечно, если вы знаете, в какой дескриптор файла вы будете печатать, просто оставьте вместо него указатель FILE*.

2 голосов
/ 22 января 2010

Тест Autoconf может проверить, записывает ли dprintf(1, "blablabla") запись в stdout или stderr, а также не удается ли dprintf(-1, "blablabla") интересным образом или пишет в stderr.

Если libc дает вам неправильный dprintf(), вы можете реализовать его самостоятельно поверх snprintf() (или даже лучше asprintf()) и write(). А затем укажите ваш файл dprintf.o для компоновщика, чтобы он предпочитал ваш файл libc.

Если вам нужно более подходящее имя, я предлагаю fdprintf().

0 голосов
/ 22 января 2010

В связи с ответом dmcer, вот ссылка на похожий вопрос, найденный здесь.

...