What is a type?

D

Dan Pop

In said:
A substantial amount of hiding is possible even so,
at the cost of one extra indirection level:

/* <stdio.h> */
typedef struct __file_magic *FILE;
...

`FILE' is now an object type (to wit, a pointer), and that
much is revealed as required. The nature of what it points
to, though, remains hidden.

Another way of hiding the internals is:

typedef unsigned char FILE[__file_size];

while the implementation operates with a completely different definition
of FILE, having the property that sizeof(__internal_FILE) == __file_size.

Even if the two types may not have the same alignment requirements, no
correct C program is going to be affected, because all valid FILE objects
are created by the implementation and are, therefore, guaranteed to be
correctly aligned.

Dan
 
K

Keith Thompson

Nope, it cannot.

2 The types declared are size_t (described in 7.17);

FILE

which is an object type ...

An incomplete type is not an object type, so your implementation would be
non-conforming.

You're right, my mistake.
I see no good reason for this requirement in the standard (private copies
of FILE objects generated by the standard C library are useless), but the
requirement is there and cannot be ignored by conforming implementations.

It's already been pointed out elsethread that the common
implementation of getc and putc as macros requires visibility to the
internals of the FILE type; there's no good way to make those
internals visible for macro expansion without making them potentially
visible to the programmer.

On the other hand (as someone else pointed out), this could probably
be handled with some sort of compiler magic.

A strictly conforming program can depend on FILE being properly
declared as an object type (by declaring a FILE object); a *sensible*
program cannot.
 
J

Jonathan Adams

Keith Thompson said:
It's already been pointed out elsethread that the common
implementation of getc and putc as macros requires visibility to the
internals of the FILE type; there's no good way to make those
internals visible for macro expansion without making them potentially
visible to the programmer.

One could, however, imagine an implementation deciding to reveal *part*
of the implementation (enough for the macros it cares about), and leaving
the rest undefined:

typedef struct __FILE_TAG {
char *_ptr;
int _cnt;
int __pad[__FILE_PAD];
} FILE;

<OT>sizeof (FILE) has to be correct mainly for those implementations
stuck with the "_iob" array said:
On the other hand (as someone else pointed out), this could probably
be handled with some sort of compiler magic.

A strictly conforming program can depend on FILE being properly
declared as an object type (by declaring a FILE object); a *sensible*
program cannot.

Of course.

- jonathan
 
C

CBFalconer

Dan said:
Eric Sosman said:
A substantial amount of hiding is possible even so,
at the cost of one extra indirection level:

/* <stdio.h> */
typedef struct __file_magic *FILE;
...

`FILE' is now an object type (to wit, a pointer), and that
much is revealed as required. The nature of what it points
to, though, remains hidden.

Another way of hiding the internals is:

typedef unsigned char FILE[__file_size];

while the implementation operates with a completely different
definition of FILE, having the property that
sizeof(__internal_FILE) == __file_size.

Even if the two types may not have the same alignment requirements,
no correct C program is going to be affected, because all valid
FILE objects are created by the implementation and are, therefore,
guaranteed to be correctly aligned.

And how do you then implement getc and putc macros, which have to
be expanded in the users code, without incorporating magic numbers
and worse? Conceded you don't have to implement them as macros.
 
D

Dan Pop

In said:
It's already been pointed out elsethread that the common

By someone with chronical problems when it comes to engaging his brain...
implementation of getc and putc as macros requires visibility to the
internals of the FILE type; there's no good way to make those
internals visible for macro expansion without making them potentially
visible to the programmer.

The decision should be left open to the implementor, rather than being
imposed on him by the standard. If the implementor doesn't provide *any*
<stdio.h> routine as a macro, why should he provide FILE as an object
type?

The (bogus) argument you're invoking works against the standard
requiring FILE to be an incomplete type, but, since this is not
the case and no one was advocating a change in this direction...

Dan
 
D

Dan Pop

In said:
Dan said:
Eric Sosman said:
Dan Pop wrote:

The type FILE is an odd case. It's intended to act like an opaque
type, in the sense that the user isn't supposed to look inside it --
and a conforming implementation probably could make it a genuine
incomplete type, hiding the actual definition inside the library
implementation.

Nope, it cannot.

2 The types declared are size_t (described in 7.17);

FILE

which is an object type ...

An incomplete type is not an object type, so your implementation
would be non-conforming.

A substantial amount of hiding is possible even so,
at the cost of one extra indirection level:

/* <stdio.h> */
typedef struct __file_magic *FILE;
...

`FILE' is now an object type (to wit, a pointer), and that
much is revealed as required. The nature of what it points
to, though, remains hidden.

Another way of hiding the internals is:

typedef unsigned char FILE[__file_size];

while the implementation operates with a completely different
definition of FILE, having the property that
sizeof(__internal_FILE) == __file_size.

Even if the two types may not have the same alignment requirements,
no correct C program is going to be affected, because all valid
FILE objects are created by the implementation and are, therefore,
guaranteed to be correctly aligned.

And how do you then implement getc and putc macros, which have to
be expanded in the users code, without incorporating magic numbers
and worse? Conceded you don't have to implement them as macros.

Where does the "without incorporating magic numbers" requirement come
from? The standard headers are already required to define plenty of
magic numbers, like FILENAME_MAX, HUGE_VAL and the whole of <limits.h>
and <float.h>.

If I am the implementor, I *know* the internal structure of the byte
array, even if I don't explicitly reveal it in <stdio.h>.

For example, if I want to implement the (nonstandard) fileno function
as a macro, I can do:

#define __handle_offset 24
#define fileno(fp) (*(int *)&(*(fp))[__handle_offset])

It's certainly less readable than

#define fileno(fp) ((fp)->handle)

but who says that standard headers must contain nice C code?

Dan
Dan
 
K

Keith Thompson

By someone with chronical problems when it comes to engaging his brain...

Yeah, whatever...
The decision should be left open to the implementor, rather than being
imposed on him by the standard. If the implementor doesn't provide *any*
<stdio.h> routine as a macro, why should he provide FILE as an object
type?

I agree.
The (bogus) argument you're invoking works against the standard
requiring FILE to be an incomplete type, but, since this is not
the case and no one was advocating a change in this direction...

I was merely speculating about why the standard requires FILE to be an
object type. Many, perhaps most, implementations do implement getc
and putc as macros; making FILE an object type makes that easier to
do. But I agree that the standard's requirement is superfluous; as
long as an implementation is *allowed* to make FILE an object type, I
can't think of a good reason for the standard to *require* it to be an
object type.

And yes, you make a valid point that I missed earlier, so don't bother
telling me to engage anything.
 
K

Keith Thompson

Where does the "without incorporating magic numbers" requirement come
from? The standard headers are already required to define plenty of
magic numbers, like FILENAME_MAX, HUGE_VAL and the whole of <limits.h>
and <float.h>.

If I am the implementor, I *know* the internal structure of the byte
array, even if I don't explicitly reveal it in <stdio.h>.

Maybe. If I'm writing an implementation of <stdio.h>, I'm allowed to
assume that the offset of a certain member is, say, 24, but I'd rather
not have to change all my magic numbers if the compiler changes the
way it lays out struct members. The compiler and the library could
be, and commonly are, implememented by two entirely different groups.
For example, if I want to implement the (nonstandard) fileno function
as a macro, I can do:

#define __handle_offset 24
#define fileno(fp) (*(int *)&(*(fp))[__handle_offset])

It's certainly less readable than

#define fileno(fp) ((fp)->handle)

but who says that standard headers must contain nice C code?

They certainly don't have to, and what you suggest is perfectly legal,
but it's nice if the code is at least maintainable if not legible.

Another way to handle this might be to define an auxiliary struct type
for the actual implementation of the FILE type, and make FILE itself
something like a character array. For example:

struct __FILE {
char *_internal_foo;
int _internal_bar;
};

typedef char FILE[sizeof(struct __FILE)];

If getc is implemented as a macro, and it needs to refer to the
_internal_foo member, it can use

((struct __FILE*)stream)->_internal_foo

There are no magic numbers, but a user program can't mess things up by
referring directly to stdin->_internal_foo.

(If the standard didn't require FILE to be an object type, the
implementation could do the same thing with "typedef void FILE;".)

On the other hand, this probably doesn't buy you all that much. Any
programmer can still write horribly non-portable code that manipulates
the internals of the FILE structure by examining the header file
(assuming it's a file) and figuring out what casts he needs to use.
Even with the internals of the FILE structure completely visible, as
they are in many (most?) implementations, most programmers are wise
enough (or ignorant enough) not to do this kind of thing.

None of this stuff (whether FILE is an object type, and what's in it
if it is) should be of any concern to any programmers who aren't
actually implementing the standard library.
 
C

CBFalconer

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

The type FILE is an odd case. It's intended to act like an opaque
type, in the sense that the user isn't supposed to look inside it --
and a conforming implementation probably could make it a genuine
incomplete type, hiding the actual definition inside the library
implementation.

Nope, it cannot.

2 The types declared are size_t (described in 7.17);

FILE

which is an object type ...

An incomplete type is not an object type, so your implementation
would be non-conforming.

A substantial amount of hiding is possible even so,
at the cost of one extra indirection level:

/* <stdio.h> */
typedef struct __file_magic *FILE;
...

`FILE' is now an object type (to wit, a pointer), and that
much is revealed as required. The nature of what it points
to, though, remains hidden.

Another way of hiding the internals is:

typedef unsigned char FILE[__file_size];

while the implementation operates with a completely different
definition of FILE, having the property that
sizeof(__internal_FILE) == __file_size.

Even if the two types may not have the same alignment requirements,
no correct C program is going to be affected, because all valid
FILE objects are created by the implementation and are, therefore,
guaranteed to be correctly aligned.

And how do you then implement getc and putc macros, which have to
be expanded in the users code, without incorporating magic numbers
and worse? Conceded you don't have to implement them as macros.

Where does the "without incorporating magic numbers" requirement come
from? The standard headers are already required to define plenty of
magic numbers, like FILENAME_MAX, HUGE_VAL and the whole of <limits.h>
and <float.h>.

.... snip about snarky ways to implement it ...

The whole point about making definitions and using them, defining
structures, etc. is to make it easy for the implementor to change
his mind, without destroying the users code. One you start playing
wierd games you make the whole system more error prone. We already
have a well known vendor of windowed OSs that does that, with
abysmal results.

So "without incorporating magic numbers" is not a requirement, but
just good practice.
 
T

Tim Rentsch

Keith Thompson said:
I wouldn't call those things "types" at all.

Yes, so you said. I was hoping you would do more in the way of
motivating, clarifying or explaining rather than just restating your
opinion.

A type is something that exists in your program.

A machine-language-level program is still a program. Other programming
languages have types. Is there something special about machine language
that the notion of types can't be used when discussing machine language
programs?

Using the term "type", in a very similar
context, to refer to a different concept can only cause confusion.

Here you're being careless with language. First I have been careful
to distinguish two different terms, "representation type" and "program
type"; analogously, consider "Java type" and "C type". Second, even
disregarding that the two terms are distinct, the presence of the word
"type" in both _could_ lead to confusion, but certainly it's not the
case that it can _only_ cause confusion - it can also lead to
clarification and deeper understanding, at least for some people, as
this thread has shown.

On an implementation where int and long have the same representation
(say, both are 32-bit two's-complement), they are distinct types. A
pointer to int and a pointer to long are distinct types. I might call
pointers that store just an address and pointers that store an address
and a length different *kinds* of pointers.

Different kinds of pointers, different types of pointers, different
forms of pointers... I did say I was looking for terminology. What
makes one of these terms better than another? And why?

Incidentally, historically the early uses of the word "type" in
programming languages were basically as synonyms for "kind".

I disagree; it's perfectly appropriate to refer to the representation
of a type. The standard does this.

I have no doubt that the standard uses the phrase "representation of a
type", or something like it. I'm just as confident that the phrase is
used as linguistic shorthand for a collective noun meaning "all of the
representations of values of the type", or perhaps "the manner in which
values of the type are represented." The evidence that this is so is
that the phrase is used only in connection with object types - it is
not used for function types or (some?) incomplete types. Right?

[...]
I don't mean "the representation of a type"; what I mean is "the type
of representation". It seems like "representation type" is a better
term for that.

Suggestions from other quarters have been "machine type",
"representational type", "implementation type", and "representation
schema". Are any of those less confusing or less misleading
than "representation type"?

Just about anything that doesn't use the word "type" for something
that isn't a C type would be better than "representation type".

Reading between the lines (and ignoring the hyperbole), it seems like
you think the word "type" should be used for, and only for, things
that are types in the C programming language. Surely most readers of
the newsgroup realize that the word type also has meaning in other
contexts; even if, given the context of comp.lang.c, we would expect
an unqualified use of "type" to be taken to mean a C source language
type, it seems reasonable to expect a qualified use of "type" to be
read according to context - "Ada parameter type", "polymorphic type",
"FORTRAN type arrays", etc. The comment given apparently disregards
this, makes no particular suggestion, and doesn't really respond to
the question posed.

That I woudn't use the same word for functions indicates that
functions and data items are very different things. On the C level,
the word "type" applies to both. On the implementation level, there
is no term that applies to both (except perhaps something vague like
"entity").

Granted, functions and data items are different things. If you want
to refer to them as "entities", that's fine with me (and in fact
"entity" is not bad as a collective term). So the question becomes,
what term should we use to mean distinct categories -- or kinds, or
forms, or types -- of the various implementation (run-time) entities
(with the understanding that the term is analogous to the notion of
"type" in C source code)?
 
R

Richard Bos

CBFalconer said:
How does this prevent the snooper from reading FILE_magic? If you
wire those definitions into the compiler, and eliminate the
existence of FILE_magic, then you have given up the flexibility of
revising the actual FILE implementation, causing attendant future
pain.

I'm afraid I don't see that. Yes, you have a bit of code apart from the
publically visible headers, but then, isn't that true for most of the
internals of any implementation? The only problem I see is that it makes
it harder to design the library and the compiler separately, but
nobody's forcing you to go that way.

Richard
 
D

Dan Pop

In said:
Dan said:
CBFalconer said:
Dan Pop wrote:
Dan Pop wrote:

The type FILE is an odd case. It's intended to act like an opaque
type, in the sense that the user isn't supposed to look inside it --
and a conforming implementation probably could make it a genuine
incomplete type, hiding the actual definition inside the library
implementation.

Nope, it cannot.

2 The types declared are size_t (described in 7.17);

FILE

which is an object type ...

An incomplete type is not an object type, so your implementation
would be non-conforming.

A substantial amount of hiding is possible even so,
at the cost of one extra indirection level:

/* <stdio.h> */
typedef struct __file_magic *FILE;
...

`FILE' is now an object type (to wit, a pointer), and that
much is revealed as required. The nature of what it points
to, though, remains hidden.

Another way of hiding the internals is:

typedef unsigned char FILE[__file_size];

while the implementation operates with a completely different
definition of FILE, having the property that
sizeof(__internal_FILE) == __file_size.

Even if the two types may not have the same alignment requirements,
no correct C program is going to be affected, because all valid
FILE objects are created by the implementation and are, therefore,
guaranteed to be correctly aligned.

And how do you then implement getc and putc macros, which have to
be expanded in the users code, without incorporating magic numbers
and worse? Conceded you don't have to implement them as macros.

Where does the "without incorporating magic numbers" requirement come
from? The standard headers are already required to define plenty of
magic numbers, like FILENAME_MAX, HUGE_VAL and the whole of <limits.h>
and <float.h>.

... snip about snarky ways to implement it ...

The whole point about making definitions and using them, defining
structures, etc. is to make it easy for the implementor to change
his mind, without destroying the users code. One you start playing
wierd games you make the whole system more error prone.

Nonsense! Every implementation has to play such "weird games" at one
point or another. See the Unix system call numbers, for a typical
example.
We already
have a well known vendor of windowed OSs that does that, with
abysmal results.

Which only proves the incompetence of that vendor.
So "without incorporating magic numbers" is not a requirement, but
just good practice.

Nonsense. If the "magic numbers" serve a well defined purpose and are
correctly handled by the implementor, they don't have any adverse effects
on anything. Furthermore, user code need not even know that they exist.

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
473,780
Messages
2,569,611
Members
45,278
Latest member
BuzzDefenderpro

Latest Threads

Top