18

How do I forward declare FILE * in C? I normally do this using struct MyType;, but naturally this doesn't appear to be possible.

If behaviour differs between C standards or compilers and with C++, this is also of interest.

Update0

Why I want to do this aside: What I'm asking is how to forward declare a non-struct/"typedef'd struct" type so that I can declare pointers to it. Obviously using void * and casting it in the source file is a bit hackish.

Matt Joiner
  • 112,946
  • 110
  • 377
  • 526
  • 4
    Why would you want to do this? – Oliver Charlesworth Oct 07 '10 at 13:57
  • 7
    #include ? – Tergiver Oct 07 '10 at 14:02
  • Its really not clear what you are trying to accomplish. Could you give some more details? – swestrup Oct 07 '10 at 14:08
  • You don't need to forward declare FILE*. Just include stdio.h. – JeremyP Oct 07 '10 at 14:49
  • Your update still doesn't explain why you want to do this. Why wouldn't you include `stdio.h`? It's not as if `stdio.h` changes frequently and is dragging down incremental build times. – jamesdlin Oct 07 '10 at 17:06
  • 5
    @Oli, @jamesdlin: It's generally good practice to avoid including headers except when they are truly needed. Including the header for a class is unnecessary if a forward declaration of the class will do. Minimizing header inclusion minimizes build time. So, trying to avoid including `` where it's not needed is simply following good practice. Unfortunately for the OP, it's not possible to do this with `FILE` because it is a `typedef`. – Dan Moulding Oct 07 '10 at 19:07
  • @Dan: As jamsedlin implied, this is something that should be particularly amenable to pre-compiled headers, etc., rather than a bunch of floating forward declarations (Don't Repeat Yourself, and all that). – Oliver Charlesworth Oct 07 '10 at 19:15
  • 1
    @Oli: Precompiled headers solve the problem in a round-about, non-standard way. Judicious use of `#include` is a direct way to reduce compile time that doesn't depend on non-standard, implementation-specific compiler features. – Dan Moulding Oct 07 '10 at 19:22
  • @Dan: Lots of stuff is likely to include `stdio.h` anyway, so IMO, avoiding it in a header file that needs it has dubious merit. If it turns out to actually matter, then break up the header file. – jamesdlin Oct 07 '10 at 19:31
  • 2
    @jamesdlin: Whether or not including `stdio.h` makes any difference is going to depend on the specific circumstances which none of us (OP excepted) are privy to. All I'm saying is that minimizing use of `#include` **in general** is a good practice and *could be*, in this case, a valid reason for the OP to explore this line of questioning. – Dan Moulding Oct 07 '10 at 19:38
  • 1
    @Dan: Minimizing use of `#include` by whipping up your prototypes for functions, types, etc you don't know is horribly *bad* practice. If you need a FILE, for *any* reason, it's because you're doing something with the stdio functions and should `#include ` *anyway*. Don't include stuff you don't use, but if you use it, *include it*. – cHao Oct 08 '10 at 00:29
  • 1
    @cHao: Nobody's talking about whipping up function prototypes. That *would* be bad practice. But forward declaration of types, when all that is needed is an incomplete type, is a good practice, and that is what it seems like the OP was trying to accomplish. Imagine a struct, defined in a header file, which has a `FILE *` as a member. Imagine further that *many* source files need to use this struct, but only *one* source file does any file I/O using the `FILE *`. In this instance being able to forward declare `FILE` would be handy (and `#include ` would otherwise be unnecessary). – Dan Moulding Oct 08 '10 at 01:21
  • 1
    @Dan: Consider that if you have many source files using a struct, one doing so in ways radically different than the others, that there's probably something quite odd (even wrong) in your design. Structs aren't just a place to store stuff from all over the app, even in C. If that `FILE *` isn't needed in 9/10 of all the code that touches that struct, maybe it shouldn't be in there in the first place. – cHao Oct 08 '10 at 04:00
  • 1
    @cHao: That's quite a sweeping assertion. So if I have a 100 file project and in that project I have a Logger class that has a private `FILE *` member, and only Logger.cpp does any file I/O using that private `FILE *`, then there's something wrong with my design because the other 99 files, all of which use `Logger`, don't do any file I/O? I hope you see my point. 99 files have to (indirectly) include `` (so it gets compiled 99 times) even though only one file actually does any file I/O. – Dan Moulding Oct 08 '10 at 12:24
  • @Dan: Yes. 99 files (that need a Logger) indirectly include ``. As they should, since the class contains a `FILE *`. You want a standard library type, you include the header that defines it; you don't half-ass a declaration. Period. I don't care if it increases build times. One shouldn't have to wonder whether you're using the standard `struct tm` or `FILE` or whatever. The `#include` not only defines the type, it says "i'm using the stuff defined by the standard library, not some custom crap with the same name" (since redefining the type would cause a compile error). – cHao Oct 08 '10 at 13:02
  • On top of all that, C header files normally don't take long at all to compile, since they should just contain declarations for types, macros and functions. (Unlike C++ headers, there aren't any templates and such to compile, and no code to generate.) So including the header shouldn't be that much of a burden. – cHao Oct 08 '10 at 13:29
  • 1
    @cHao: @Dan Moulding is on the money. The struct is context, passed to functions defined in many source files. Only one source file operates on the member `FILE *`. In fact, the context struct is similarly forward declared in the public interface to the library, so the internals are hidden. I don't need or want the other source files to contain IO routines. If the `FILE *` wasn't forward declared, a user defined type containing it would be, which would imply additional memory allocation and potential bugs. – Matt Joiner Oct 08 '10 at 14:13
  • @cHao: I think it's quite obvious by now why it would be nice if you *could* forward declare `FILE`, and is the reason this question came up -- just as I was explaining to @Oli and @jamedlin. Unfortunately, you can't forward declare `typedef`s. But I'll say it again: using forward declarations, *when all that is needed is an incomplete type*, is a smart practice. Opting to instead include the entire definition of a struct via inclusion of its header is unnecessary and wasteful (albeit common practice, because it's easier than thinking about whether or not just the incomplete type will do). – Dan Moulding Oct 08 '10 at 18:13
  • @Dan: And easier than explaining why you have a type that's not obviously defined anywhere. And easier than having to remember whether you *have* the incomplete type or the real deal. Sorry, but no. Forward declarations are for specifying the API in a usable place, and for fixing the problem of circular dependencies. They're not for shaving a second off your build time. – cHao Oct 08 '10 at 18:42
  • @cHao: See http://bit.ly/7frLLP, http://bit.ly/9qtWq7, http://bit.ly/cNLoWh, http://bit.ly/9FHb7E, http://bit.ly/bxErrJ, and countless others. – Dan Moulding Oct 08 '10 at 19:36
  • @Dan: Note that *every one of those examples* is for C++. C++ headers (especially ones with templates) can admittedly add a lot to compile time, and it might be worthwhile to skip them if possible. But also note that all of those examples of forward declaration are for custom classes. *Not one of them* mentions forward declaring any type in either the C or C++ standard libraries. – cHao Oct 08 '10 at 20:02
  • @cHao: Build time is not the issue in my situation. Please focus on the IO routines, and the namespace clutter I don't want in the other source files. The only valid point I think you've made is regarding mistakenly interpreting an incomplete type in the wrong way. If you do this you have bigger issues to worry about. Also I imagine things will explode in a ball of flames at runtime. It's definitely not a concern in C++. – Matt Joiner Oct 09 '10 at 00:38

6 Answers6

25

You can't. The standard just states that FILE is "an object type capable of recording all the information needed to control a stream"; it's up to the implementation whether this is a typedef of a struct (whose name you don't know anyway), or something else.

The only portable way to declare FILE is with #include <stdio.h> (or <cstdio> in C++).

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • 2
    If you absolutely cannot include `stdio.h`, the only alternative is to store pointers to `void` in place of `FILE *`, and then other modules will have to convert them back and forth to `FILE *` to use them. – R.. GitHub STOP HELPING ICE Oct 07 '10 at 16:03
  • 2
    I think `FILE` is necessarily a `typedef`. It cannot be a `struct`, because then it would be `struct FILE`, not just `FILE`. – Dan Moulding Oct 07 '10 at 19:10
  • @Dan: yes, you're right, in C at least. My brain was stuck in C++. – Mike Seymour Oct 07 '10 at 19:39
  • Please quote the relevant part of the standard (for C) that suggests FILE can be "something else", or what that something else is. – Matt Joiner Oct 08 '10 at 08:19
  • @Matt: The quote I gave (from 7.19.1/2) says that `FILE` is an "object type", so it could be any object type (arithmetic, structure, union, array, or pointer). It's up to the library implementor to choose the type that works best for them. – Mike Seymour Oct 08 '10 at 11:07
  • Given that `FILE` is a typedef of a struct on all implementations I'm aware of, can you explain why it's not possible to forward declare this? – Matt Joiner Oct 08 '10 at 14:17
  • 2
    @Matt: Two reasons: you are not aware of all current and future implementations, and there is no standard name for the struct (since the standard doesn't specify that it's a struct at all). So, even if you do assume that it will be a struct on every implementation you care to support, you can't forward declare the struct since you don't know its name. – Mike Seymour Oct 08 '10 at 14:27
  • @Mike Seymour: Can you quote the standard on forward declaring struct typedefs? – Matt Joiner Oct 09 '10 at 00:44
  • @Matt: The standard says nothing about forward declaring typedefs, because there is no such concept. As I said (with quotes) in another comment, the only way to introduce a typedef name is in a typedef declaration, which has to include the type specifier. I'm afraid you really do have to include the header file, and put up with the extra milliseconds of compilation time. – Mike Seymour Oct 09 '10 at 16:52
11

If you #include <stdio.h> you should get the FILE typedef with it. That's the only really safe and portable way -- you can't have a typedef without a type to alias, and there's no guarantee about what type FILE aliases, so every compiler or libc or whatever can have a different one. But you'd need the type to be correct in case anyone actually wants to #include <stdio.h>, lest the inconsistent definitions cause an error.

Edit:

Now that i think about it, there might be one other way i can think of. It's not a typedef, it's evil macro stuff that works by hijacking the definition of "FILE". I wouldn't recommend it for that reason alone. But it might work for what you need.

#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

Then #define USES_REAL_FILE_TYPE before you include the file in any code where you need a real FILE *, and the rest of the code will just see the pointer as a void *.

I make no guarantees that this won't mess stuff up. In particular, it will break in any case where you want to know anything real about such a fake type, and all code that touches the pointer may need that #define. But if you're dead set against "unnecessary" #includes, it's the only way you're gonna get a FILE * without interfering with stdio. You're not going to be able to forward declare a typedef.

Edit2:

OK, i checked just to make sure. Not sure how standard it is,or what you can do with it, but...

typedef FILE;

works in Visual C and GCC both, but only when compiling C code. It would appear that the C++ standard explicitly says somewhere that you can't have a typedef without a type. The C one, though, doesn't.

However, it doesn't seem to allow a type to be forward declared, not in GCC anyway. If you try to typedef int FILE; right afterward, it throws an error about conflicting typedefs. VS, however, seems to allow it, as long as it's to an integer type. Seems typedef X really means typedef int X in VS (and apparently, in C99). Either way, GCC won't let you redo the typedef, even to the exact same type.

cHao
  • 84,970
  • 20
  • 145
  • 172
  • Please justify why it's the only portable way. – Matt Joiner Oct 08 '10 at 08:17
  • 4
    Because in order to declare `FILE`, you'd have to know what type it is behind the scenes, lest everyone who actually *did* `#include ` get a compile error (because the two types don't match up). Thing is, though, the actual type behind a `FILE` is not specified by any standard, so every compiler could have a different one. – cHao Oct 08 '10 at 10:44
  • Well I can't downvote you, because you're not wrong, but this isn't the answer I wanted. Perhaps you could explain why it's not possible to forward declare a typedef - quoting the standard...? – Matt Joiner Oct 08 '10 at 14:15
  • 1
    @Matt: The standard doesn't explicitly say that you can't; it just provides no way of doing it. 6.7.7 describes the only way to introduce a typedef name: "In a declaration whose storage-class specifier is typedef, each declarator defines an identifier to be a typedef name that denotes the type specified for the identifier". 6.7.2/2 says "At least one type specifier shall be given in the declaration specifiers in each declaration", so it's impossible to declare the typedef name without specifying its type. – Mike Seymour Oct 08 '10 at 15:12
  • @Matt Joiner: Updated. It's a bit ugly, but not as ugly as casting everywhere. – cHao Oct 08 '10 at 20:40
  • @cHao: I considered that something like this was possible. It's actually much nicer if you confine this to the source file with the IO: put typedef `void *FILE_forward;` in the struct, and `#define FILE_forward FILE *` in the source file. I'd still prefer a definitive answer that explains why I can't forward declare a typedef (from the standard). – Matt Joiner Oct 09 '10 at 00:41
  • @Matt: Updated again. Don't ask me how or why, but... :) – cHao Oct 09 '10 at 02:44
8

FILE is a typedef around a struct you're not supposed to explore too much (like you are not supposed to play with the data behind a WinAPI Handle) unless through its dedicated API function.

Forward-declaring?

Forward-declaring enables one to declare a pointer (or on C++, a reference) to the type and having that declaration compile as long as the symbol is not used (for example, forward-declaring a symbol in your header, and then including the header where the symbol is properly declared in the source using it).

So, forward-declaring includes means:

  • faster compilation
  • less coupling

Chuck Typedef vs. Forward-declaring?

The problem with typedefs is that they are a pain to handle, because, as you discovered, you can't forward-declare them.

So you can't forward-declare FILE, nor you can forward-declare std::string. So you have no choice but including the header to handle them.

(This is the reason I hate the typedef struct { /* ... */ } MyTypedefedType ; pattern from C invading C++ code: It's useless in C++, and it prevents forward-declaration.)

Forward-declaring standard symbols?

The good part is that if the symbols are "standards", it should not be too much painful to include their header. The coupling is not so much problem, and if it will slow the compilation somewhat, even that can be made painless through the use of precompiled headers.

<iosfwd> : Some people thought about you!

The C++ standard library offers the <iosfwd> header.

Instead of including any (or all) the C++ streams headers, you can include <iosfwd> if what you need is only forward declaration.

Community
  • 1
  • 1
paercebal
  • 81,378
  • 38
  • 130
  • 159
4

FILE is a system-dependent typedef. You are not supposed to care how the actual structure is defined or even named. But you can always look into your /usr/include/stdio.h file :)

Nikolai Fetissov
  • 82,306
  • 11
  • 110
  • 171
  • This seems to be the best solution. That or @R..'s suggestion that a void * is used, and casting done. Unfortunately, using the system defined struct names is far more effort than it's worth. – Matt Joiner Oct 08 '10 at 08:20
  • @Matt: If you do copy the declaration from your system header (which I would advise against for portability reasons), bear in mind that duplicate `typedef` declarations aren't allowed in C, although they are in C++. This makes it an error to include both your declaration and `` in the same C compilation unit. – Mike Seymour Oct 08 '10 at 15:46
  • So a good idea when implementing a C library is to let all internal types be a struct. Then predeclaration will work. Isn't libpng implemented that way? So instead of typedef void* handle; write struct handle; in header and struct handle{void* h_impl;}; in source – user877329 May 30 '12 at 11:38
0

As already pointed out, there is no portable way to forward declare the FILE structure or type definition.

However, one can change the interface of the own facility to rely on plain integers, and then use the fileno function (also available via #include <stdlib.h>).

Detailed steps
0. Locate your current interface. For example:
    void myprint(FILE* stream, ...);
1. Use an integer file descriptor (fd) instead of FILE*:
    void myprint(int stream_fd, ...);
2. Call new interface with fileno instead of FILE*:
    myprint(fileno(stream));

The disadvantage however is that your implementation (myprint in the above example) needs to be rewritten by using a file descriptor instead of FILE* for the actual I/O routines. An alternative to rewriting the implementation is to simply fdopen a FILE using the given descriptor.

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

The above in turn causes you thinking about where "to own" the resource in order to close the FILE when not needed anymore. Often an open/close sequence is just fine (as shown above), however in more complicated cases one needs to adjust the implementation (which we tried to avoid) by opening the file using append mode, etc.

hfp
  • 17
  • 2
-4

FILE* is an opaque type. Thus, this should in theory work.

typedef struct FILE_impl_but_including_stdio_h_is_best FILE;
Maister
  • 4,978
  • 1
  • 31
  • 34
  • 1
    You'd think wouldn't you. But it's not necessarily an opaque structure. If you have access to /usr/include/stdio.h on a Macintosh, you'll see it is not opaque at all. – JeremyP Oct 07 '10 at 14:48
  • 1
    That might be the case, but then again, a pointer is a pointer (technically not quite correct, but hey). The stdio only cares about FILE*. – Maister Oct 07 '10 at 14:57
  • 5
    This will cause an error if you later include `stdio.h` in any file where this typedef is visible. – R.. GitHub STOP HELPING ICE Oct 07 '10 at 16:05