views:

315

answers:

5

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.

+10  A: 

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
Please justify why it's the only portable way.
Matt Joiner
Because in order to declare `FILE`, you'd have to know what type it is behind the scenes, lest everyone who actually *did* `#include <stdio.h>` 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
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
@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
@Matt Joiner: Updated. It's a bit ugly, but not as ugly as casting everywhere.
cHao
@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
@Matt: Updated again. Don't ask me how or why, but... :)
cHao
+3  A: 

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 N Fetissov
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
@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 `<stdio.h>` in the same C compilation unit.
Mike Seymour
+10  A: 

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
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..
I think `FILE` is necessarily a `typedef`. It cannot be a `struct`, because then it would be `struct FILE`, not just `FILE`.
Dan Moulding
@Dan: yes, you're right, in C at least. My brain was stuck in C++.
Mike Seymour
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
@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
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
@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
@Mike Seymour: Can you quote the standard on forward declaring struct typedefs?
Matt Joiner
@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
A: 

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

typedef struct FILE_impl_but_including_stdio_h_is_best FILE;
Maister
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
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
This will cause an error if you later include `stdio.h` in any file where this typedef is visible.
R..
Unfortunately this doesn't work, as per @R..'s comment.
Matt Joiner
A: 

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.

paercebal