Переслать объявление FILE *

Как переслать объявление FILE * в C? Обычно я делаю это с помощью struct MyType;, но, естественно, это не представляется возможным.

Если поведение отличается от стандартов C или компиляторов и с С++, это также представляет интерес.

Update0

Почему я хочу сделать это в стороне: то, что я прошу, - это переадресовать объявление типа структуры неструктурированного типа типа typedef'd, чтобы я мог объявить ему указатели. Очевидно, использование void * и отбрасывание его в исходном файле немного хакерское.

Ответ 1

Вы не можете. Стандарт просто указывает, что FILE - это "тип объекта, способный записывать всю информацию, необходимую для управления потоком"; это до реализации, является ли это typedef для struct (имя которого вы вообще не знаете) или что-то еще.

Единственный переносимый способ объявить FILE - с #include <stdio.h> (или <cstdio> в С++).

Ответ 2

Если вы #include <stdio.h>, вы должны получить FILE typedef с ним. Это единственный действительно безопасный и переносимый способ - у вас не может быть typedef без типа для псевдонима, и нет никакой гарантии о том, какие типы FILE aliases, поэтому каждый компилятор или libc или что-то другое может иметь другое. Но вам нужно, чтобы тип был правильным, если кто-то действительно хочет #include <stdio.h>, чтобы несовместимые определения вызывают ошибку.

Edit:

Теперь, когда я думаю об этом, может быть еще один способ, о котором я могу думать. Это не typedef, это злой макрос, который работает, захватив определение "ФАЙЛ". Я бы не рекомендовал его по этой причине. Но это может сработать для того, что вам нужно.

#ifdef USES_REAL_FILE_TYPE
#include <stdio.h>
#else
#define FILE void
#endif

/* declare your struct here */

#ifndef USES_REAL_FILE_TYPE
#undef FILE
#endif

Затем #define USES_REAL_FILE_TYPE перед включением файла в любой код, где вам нужен реальный FILE *, а остальная часть кода будет видеть указатель как void *.

Я не гарантирую, что это не повредит. В частности, он будет разбит в любом случае, когда вы хотите узнать что-нибудь реальное о таком поддельном типе, и для всего кода, который касается указателя, может потребоваться это #define. Но если вы мертвы против "ненужных" # включений, это единственный способ получить FILE *, не вмешиваясь в stdio. Вы не сможете переслать объявление typedef.

Edit2:

ОК, я проверил только, чтобы убедиться. Не знаете, насколько это стандартно, или что вы можете с ним делать, но...

typedef FILE;

работает в Visual C и GCC, но только при компиляции кода C. Похоже, что в стандарте С++ явно сказано где-то, что вы не можете иметь typedef без типа. Тем не менее, C не делает.

Однако, похоже, это не означает, что тип будет объявлен вперед, а не в GCC. Если вы попытаетесь выполнить typedef int FILE; сразу после этого, он выдает ошибку о конфликтующих typedefs. VS, однако, похоже, позволяет это, если это целочисленный тип. Кажется, typedef X действительно означает typedef int X в VS (и, по-видимому, в C99). В любом случае, GCC не позволит вам повторить typedef, даже до того же самого типа.

Ответ 3

FILE - это typedef вокруг структуры, которую вы не должны слишком много изучать (например, вы не должны играть с данными за ручкой WinAPI), если только через свою выделенную функцию API.

Прогнозные декларирование?

Forward-declaring позволяет объявить указатель (или на С++, ссылку) к типу и скомпилировать это объявление до тех пор, пока символ не будет использоваться (например, forward-declaring symbol в вашем заголовке и затем включающий заголовок, где символ правильно объявлен в источнике, используя его).

Таким образом, форвардное объявление включает в себя:

  • более быстрая компиляция
  • меньше связи

Chuck Typedef vs. Forward-declaring?

Проблема с typedefs заключается в том, что они являются болью для обработки, потому что, как вы обнаружили, вы не можете переслать-объявить их.

Таким образом, вы не можете переадресовать-объявить FILE, и вы не можете переслать-объявить std::string. Таким образом, у вас нет выбора, кроме заголовка для их обработки.

(Вот почему я ненавижу шаблон typedef struct { /* ... */ } MyTypedefedType ; от C вторжения кода С++: он бесполезен в С++ и предотвращает форвардную декларацию.)

Форвард-декларирование стандартных символов?

Хорошая часть состоит в том, что, если символы являются "стандартами", не должно быть слишком больно включать их заголовок. Связь не так много, и если она немного замедлит компиляцию, даже это может стать безболезненным благодаря использованию предварительно скомпилированных заголовков.

<iosfwd>: Некоторые думали о вас!

Стандартная библиотека С++ предлагает заголовок <iosfwd>.

Вместо включения каких-либо (или всех) заголовков потоков С++ вы можете включить <iosfwd>, если вам нужно только объявление вперед.

Ответ 4

FILE является системно-зависимым typedef. Вы не должны заботиться о том, как фактическая структура определена или даже названа. Но вы всегда можете заглянуть в свой /usr/include/stdio.h файл:)

Ответ 5

Как уже указывалось, нет переносного способа перенаправления объявления структуры FILE или определения типа.

Однако можно изменить интерфейс собственного объекта, чтобы полагаться на простые целые числа, а затем использовать функцию fileno (также доступную через #include <stdlib.h>).

Подробные шаги
0. Найдите текущий интерфейс. Например:
    void myprint(FILE* stream, ...);
1. Используйте дескриптор целочисленного файла (fd) вместо FILE*:
     void myprint(int stream_fd, ...);
2. Вызовите новый интерфейс с помощью fileno вместо FILE*:
     myprint(fileno(stream));

Однако недостатком является то, что ваша реализация (myprint в приведенном выше примере) должна быть переписана с использованием файлового дескриптора вместо FILE* для реальных процедур ввода-вывода. Альтернативой перезаписи реализации является просто fdopen a FILE с использованием данного дескриптора.

void myprint(int stream_fd, ...)
{
  FILE *const stream = fdopen(stream_fd);
  /* your existing implementation follows */
  fclose(stream);
}

Вышеизложенное, в свою очередь, заставляет задуматься о том, где "владеть" ресурсом, чтобы закрыть FILE, когда он больше не нужен. Часто последовательность открывания/закрывания просто прекрасна (как показано выше), однако в более сложных случаях нужно настроить реализацию (которую мы пытались избежать), открыв файл с помощью режима добавления и т.д.

Ответ 6

FILE * - непрозрачный тип. Таким образом, это должно теоретически работать.

typedef struct FILE_impl_but_including_stdio_h_is_best FILE;