Forward declare FILE *

2019-01-18 03:56发布

问题:

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.

回答1:

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++).



回答2:

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.



回答3:

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.



回答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 :)



回答5:

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.



回答6:

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

typedef struct FILE_impl_but_including_stdio_h_is_best FILE;