Вперед объявить ФАЙЛ * - PullRequest
       17

Вперед объявить ФАЙЛ *

16 голосов
/ 07 октября 2010

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

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

Update0

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

Ответы [ 6 ]

25 голосов
/ 07 октября 2010

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

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

11 голосов
/ 07 октября 2010

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

Edit:

Теперь, когда я думаю об этом, я могу думать о другом. Это не typedef, это злой макрос, который работает, угоняя определение «FILE». Я не рекомендовал бы это по одной только этой причине. Но это может сработать для того, что вам нужно.

#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. Но если вы настроены против «ненужного» #include, это единственный способ получить FILE * без вмешательства в stdio. Вы не сможете перенаправить объявление typedef.

Edit2:

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

typedef FILE;

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

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

6 голосов
/ 09 октября 2010

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

Объявление вперед?

Объявление вперед позволяет объявлять указатель (или на C ++, ссылку) на тип и скомпилировать это объявление до тех пор, пока символ не используется (например, объявление вперед)символ в вашем заголовке, а затем включающий заголовок, где символ должным образом объявлен в источнике, использующем его).

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

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

Чак Typedef против объявления вперед?

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

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

(Это причина, по которой я ненавижу шаблон typedef struct { /* ... */ } MyTypedefedType ; из C, внедряющего код C ++: он бесполезен в C ++ и предотвращает пересылку-declaration.)

Стандартные символы, объявленные в прямом направлении?

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

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

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

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

4 голосов
/ 07 октября 2010

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

0 голосов
/ 06 января 2016

Как уже указывалось, не существует переносимого способа прямого объявления структуры 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, когда он больше не нужен. Часто последовательность открытия / закрытия вполне подходит (как показано выше), однако в более сложных случаях необходимо настроить реализацию (которую мы пытались избежать), открыв файл в режиме добавления и т. Д.

0 голосов
/ 07 октября 2010

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

typedef struct FILE_impl_but_including_stdio_h_is_best FILE;
...