opaque style question

J

Joe Wright

Dan Pop wrote:

[ snip ]
I've never understood why FILE can't be an incomplete type, as long as
there is *nothing* useful user code can do with an object of type FILE.
I agree there is no member of the FILE structure that is interesting
to the C programmer. But if FILE were incomplete..

FILE *in = stdin, *out = stdout;

...might not work.
 
C

CBFalconer

Joe said:
Dan Pop wrote:

[ snip ]
I've never understood why FILE can't be an incomplete type, as
long as there is *nothing* useful user code can do with an
object of type FILE.
I agree there is no member of the FILE structure that is
interesting to the C programmer. But if FILE were incomplete..

FILE *in = stdin, *out = stdout;

..might not work.

and why not? The thing that won't work is:

FILE in = *stdin;

but you can write such statements with impunity in most systems,
although they won't do you much good.
 
I

Irrwahn Grausewitz

Joe Wright said:
I agree there is no member of the FILE structure that is interesting
to the C programmer. But if FILE were incomplete..

FILE *in = stdin, *out = stdout;

..might not work.

How so?

Regards
 
J

Joe Wright

CBFalconer said:
Joe said:
Dan Pop wrote:

[ snip ]
I've never understood why FILE can't be an incomplete type, as
long as there is *nothing* useful user code can do with an
object of type FILE.

I agree there is no member of the FILE structure that is
interesting to the C programmer. But if FILE were incomplete..

FILE *in = stdin, *out = stdout;

..might not work.


and why not? The thing that won't work is:

FILE in = *stdin;

but you can write such statements with impunity in most systems,
although they won't do you much good.
As both stdin and stdout have type 'pointer to FILE' and FILE is
incomplete, what values would be assigned to in and out?
 
I

Irrwahn Grausewitz

Joe Wright said:
Irrwahn said:
Joe Wright said:
[...] if FILE were incomplete..

FILE *in = stdin, *out = stdout;

..might not work.

How so?

As both stdin and stdout have type 'pointer to FILE' and FILE is
incomplete, what values would be assigned to in and out?

The respective values of stdin and stdout. Nothing prevents
an implementation to declare stdin et al. as extern FILE *.

Regards
 
C

CBFalconer

Joe said:
CBFalconer said:
Joe said:
Dan Pop wrote:

[ snip ]

I've never understood why FILE can't be an incomplete type, as
long as there is *nothing* useful user code can do with an
object of type FILE.

I agree there is no member of the FILE structure that is
interesting to the C programmer. But if FILE were incomplete..

FILE *in = stdin, *out = stdout;

..might not work.

and why not? The thing that won't work is:

FILE in = *stdin;

but you can write such statements with impunity in most systems,
although they won't do you much good.
As both stdin and stdout have type 'pointer to FILE' and FILE is
incomplete, what values would be assigned to in and out?

That's the point. FILE is _not_ incomplete on any system known to
me, in order to allow putc and getc macros to function. Thus you
can write that FILE in = *stdin with impunity on those systems,
and get no errors. If you then try to pass &in to various
functions, peculiar things are going to happen.

I guess a fairly harmless test of this would be:

#include <stdio.h>

/* proving FILE is not incomplete on this system */
int main(void)
{
printf("sizeof(FILE) = %d\n", (int)sizeof(FILE));
return 0;
}
 
A

Arthur J. O'Dwyer

[Agreed.]

[Of course it would work.]

[All systems, because FILE is defined to be an object type.]
That's the point. FILE is _not_ incomplete on any system known to
me, in order to allow putc and getc macros to function. Thus you
can write that FILE in = *stdin with impunity on those systems,
and get no errors.

I think most participants in this thread aren't understanding each
other. FILE is not incomplete on any system because *the Standard
says it isn't*! This has nothing to do with the working or not of
putc and getc macros. It's perfectly possible to write macros that
work with incomplete types, for example:

#define FIRST_ELEM(a) (a[0])

where 'a' can be declared with type 'int[]'; or with pointers to
incomplete types, for example:

#define MY_GETC(fp) (fgetc(fp))

where 'fp' is defined with object type 'MY_FILE *' and 'MY_FILE'
is incomplete.
If you then try to pass &in to various
functions, peculiar things are going to happen.

Probably. That's not surprising at all; the value of &in, while
it has the right type (FILE *), was not derived from a call to
'fopen', nor is it equal to 'stdin', 'stdout', 'stderr', or any
other pre-defined 'FILE *' object. So it's basically a garbage
value. Garbage in, garbage out.
I guess a fairly harmless test of this would be:

#include <stdio.h>

/* proving FILE is not incomplete on this system */
int main(void)
{
printf("sizeof(FILE) = %d\n", (int)sizeof(FILE));
return 0;
}

You really could have removed the first and last four lines of that
program, and you still would have had a proof that 'FILE' is not an
incomplete type. The Standard *says* it's not.

-Arthur
 
B

Barry Schwarz

Joe said:
CBFalconer said:
Joe Wright wrote:
Dan Pop wrote:

[ snip ]

I've never understood why FILE can't be an incomplete type, as
long as there is *nothing* useful user code can do with an
object of type FILE.

I agree there is no member of the FILE structure that is
interesting to the C programmer. But if FILE were incomplete..

FILE *in = stdin, *out = stdout;

..might not work.

and why not? The thing that won't work is:

FILE in = *stdin;

but you can write such statements with impunity in most systems,
although they won't do you much good.
As both stdin and stdout have type 'pointer to FILE' and FILE is
incomplete, what values would be assigned to in and out?

That's the point. FILE is _not_ incomplete on any system known to
me, in order to allow putc and getc macros to function. Thus you
can write that FILE in = *stdin with impunity on those systems,

Is there any reason that FILE could not be the typedef for an array.
In such a case, the assignment would be illegal.
and get no errors. If you then try to pass &in to various
functions, peculiar things are going to happen.

I guess a fairly harmless test of this would be:

#include <stdio.h>

/* proving FILE is not incomplete on this system */
int main(void)
{
printf("sizeof(FILE) = %d\n", (int)sizeof(FILE));
return 0;
}



<<Remove the del for email>>
 
J

Joe Wright

CBFalconer said:
Joe said:
CBFalconer said:
Joe Wright wrote:

Dan Pop wrote:

[ snip ]


I've never understood why FILE can't be an incomplete type, as
long as there is *nothing* useful user code can do with an
object of type FILE.

I agree there is no member of the FILE structure that is
interesting to the C programmer. But if FILE were incomplete..

FILE *in = stdin, *out = stdout;

..might not work.

and why not? The thing that won't work is:

FILE in = *stdin;

but you can write such statements with impunity in most systems,
although they won't do you much good.

As both stdin and stdout have type 'pointer to FILE' and FILE is
incomplete, what values would be assigned to in and out?


That's the point. FILE is _not_ incomplete on any system known to
me, in order to allow putc and getc macros to function. Thus you
can write that FILE in = *stdin with impunity on those systems,
and get no errors. If you then try to pass &in to various
functions, peculiar things are going to happen.

I guess a fairly harmless test of this would be:

#include <stdio.h>

/* proving FILE is not incomplete on this system */
int main(void)
{
printf("sizeof(FILE) = %d\n", (int)sizeof(FILE));
return 0;
}
We may be going sideways here. I was responding to Dan's conjecture
on whether FILE could/should be incomplete so as to be opaque. While
you can declare a pointer to incomlete struct type, it would be
problematic to assign meaningful values to such pointers.

I'm perfectly happy that FILE is not incomplete. The only time I
might even look at it is on Sunday afternoon of a really dismal day
on which I am terminally bored. And then I can't really make sense
of it. It might as well be opaque.
 
C

CBFalconer

Arthur J. O'Dwyer said:
.... snip ...

You really could have removed the first and last four lines of
that program, and you still would have had a proof that 'FILE'
is not an incomplete type. The Standard *says* it's not.

After all the fooferaw and the above I actually went and read the
appropriate bits of N869. You are right. All we can say is that
nothing within FILE is defined, or that everything within FILE is
implementation defined.

This proves it is a terrible example of opaque objects. :) But
be of good cheer, I gave a better example from my hashlib package
earlier. Having built that, I claim to know what it is.
 
A

Arthur J. O'Dwyer

We may be going sideways here. I was responding to Dan's conjecture
on whether FILE could/should be incomplete so as to be opaque. While
you can declare a pointer to incomlete struct type, it would be
problematic to assign meaningful values to such pointers.

No, it wouldn't. Look, what do you do with C's objects of type
'FILE *' at the moment? You can:

- Assign predefined values to them: fp = stdin;
- Assign new values to them: fp = fopen(...);
- Pass them to functions: fclose(fp);
- Pass their addresses to functions: my_open(&fp);
- Compare them for equality: if (fp == stdin) ...
- Compare them to predefined values: if (fp == NULL) ...

What other meaningful operations on file descriptors can you think
of?* Note that all of the above operations could be done whether
FILE were an opaque type or not. As it happens, FILE is *not* an
opaque type, but I agree with Dan Pop here --- there's no practical
reason for the Standard to say it mustn't be. It looks like just
one of C's "quirks."

-Arthur

[*] - I know the phrase "file descriptor" usually means something
else on Unixoid systems, but if I said "file-describer," I'd get
responses correcting my English. ;-) Standard C, as far as I know,
doesn't use the term "file descriptor" to mean either an integer
*or* a [pointer to a] FILE object.
 
K

Keith Thompson

CBFalconer said:
After all the fooferaw and the above I actually went and read the
appropriate bits of N869. You are right. All we can say is that
nothing within FILE is defined, or that everything within FILE is
implementation defined.

It's defined by the implementation, but it's not
"implementation-defined" (i.e., the implementation isn't required to
document it).
 
D

Dan Pop

In said:
Time to engage yours. :)

It was already engaged.
The macro is expanded by the user in
his own little world. How does that expansion get at the various
fields of the FILE structure if that structure is hidden? Are you
recommending the implementor evaluate offsetof(...) for every
field in FILE and insert corresponding absolute values into his
macros? Along with the appropriate casts. Sounds like a
maintenance nightmare to me.

A simple cast is enough for the job. A conversion between pointer to
incomplete type and pointer to the real thing. Something along the
lines:

struct __FILE { /* definition of the real thing */ };
typedef struct __FOO FILE; /* struct __FOO never defined */

Now, FILE is an incomplete type, but a pointer to FILE can be converted
to a pointer to __FILE and vice versa.

Dan
 
D

Dan Pop

In said:
We may be going sideways here. I was responding to Dan's conjecture
on whether FILE could/should be incomplete so as to be opaque. While
you can declare a pointer to incomlete struct type, it would be
problematic to assign meaningful values to such pointers.

Not at all. You take a pointer to a complete struct type (the real thing)
and convert it to a pointer to an incomplete struct type. You do the
opposite conversion when actually trying to use the value stored in the
pointer to incomplete struct type.

But it needn't be an incomplete struct in the first place. Consider

typedef void FILE;

The *only* reason a conforming implementation cannot do that is because
the standard wants an object type for FILE. For diagnosing purposes,
such a definition for FILE would not be optimal (you could pass any object
or incomplete pointer type where a FILE pointer is expected), but
the entire <stdio.h> specification could be implemented without any
problems with FILE defined like this.

Dan
 
C

CBFalconer

Dan said:
It was already engaged.


A simple cast is enough for the job. A conversion between pointer
to incomplete type and pointer to the real thing. Something along
the lines:

struct __FILE { /* definition of the real thing */ };
typedef struct __FOO FILE; /* struct __FOO never defined */

Now, FILE is an incomplete type, but a pointer to FILE can be
converted to a pointer to __FILE and vice versa.

How does that prevent the user from writing something of the form:

fp->fieldname = whatever;

where fp is a FILE* secured from fopen? Maybe that thing is,
strictly speaking, an incomplete type (although I have my doubts),
but it is not opaque, which is the subject of this thread. The
purpose of all these gyrations is to protect various entities from
prying.
 
E

E. Robert Tisdale

CBFalconer said:
How does that prevent the user from writing something of the form:

fp->fieldname = whatever;

where fp is a FILE* secured from fopen?
Maybe that thing is, strictly speaking, an incomplete type
(although I have my doubts) but it is not opaque,
which is the subject of this thread.
The purpose of all these gyrations
is to protect various entities from prying.

No! You are confused.

The purpose of an opaque type
is to help prevent the application programmers from
*accidental* direct references.
It is *not* encryption.
A dedicated hacker can usually determine the private definition
of an opaque data type by doing a few simple tests.
 
A

Arthur J. O'Dwyer

How does that prevent the user from writing something of the form:

fp->fieldname = whatever;

where fp is a FILE* secured from fopen?

Let's see. FILE is a typedef for struct __FOO, and struct __FOO
is never defined in the user's translation unit. Thus, it has no
'fieldname' available; thus, constraint violation or syntax error
or something. Invalid program, anyway.
The user *could* follow the lead of 'getc'/'putc' and write

((struct __FILE *)fp)->fieldname = whatever;

but now not only is he *obviously* *intentionally* breaking the
rules, he's introducing a big ugly cast in his code, which is a
tipoff that something dubious is happening.
Maybe that thing is,
strictly speaking, an incomplete type (although I have my doubts),
but it is not opaque, which is the subject of this thread. The
purpose of all these gyrations is to protect various entities from
prying.

If "various entities" can read the <stdio.h> header and relevant
implementation specs, and apply offsetof and pointer arithmetic, they
can get around any opaqueness at all. What exactly are you trying
to prevent?

(Example: I can "get around" anything at all by applying an expression
of the form

*(char *)my_address = my_num;

I don't think you'd consider this a big hole in opaqueness. So what
*do* you consider a problem?)

-Arthur
 
C

CBFalconer

Arthur J. O'Dwyer said:
.... snip ...

If "various entities" can read the <stdio.h> header and relevant
implementation specs, and apply offsetof and pointer arithmetic,
they can get around any opaqueness at all. What exactly are you
trying to prevent?

(Example: I can "get around" anything at all by applying an
expression of the form

*(char *)my_address = my_num;

I don't think you'd consider this a big hole in opaqueness. So
what *do* you consider a problem?)

I don't consider FILE to be opaque in the first place. Obscure,
yes. Things are properly opaque when the user can't get at
anything by reading the header; i.e. he requires the source code
in addition. Then the source can be freely modified without
affecting the header, and the maximum effect on the user code is a
need for relinking.
 
D

Dan Pop

In said:
I don't consider FILE to be opaque in the first place. Obscure,
yes. Things are properly opaque when the user can't get at
anything by reading the header;

Who said the user can read the header? Chapter and verse, please.

Anyway, *your* definition of type opacity is at odds with the common one:
a type whose definition is not documented (C header files don't count as
documentation). FILE is the canonical C example of opaque type. As usual
C helps people to avoid shooting themselves in the foot but doesn't even
try to prevent them from doing that.

Dan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,141
Messages
2,570,817
Members
47,366
Latest member
IanCulpepp

Latest Threads

Top