incomplete types

M

Mark

I'm wondering if it is OK to declare incomplete type like this:

/* foo.h */
#ifndef FOO_H
#define FOO_H

typedef struct foo foo_t;

#endif

/* foo.c */
#include "foo.h"

struct foo {
int x;
int y;
};
...

Or it is necessary to make forward structure declaration, as in:
/* foo.h */
struct foo;
typedef struct foo foo_t;

while 'foo.c' defines the structure members and the like. What's the big difference with these? They compile perfectly well.

Thanks in advance.

--
Mark
 
R

Richard Bos

Mark said:
SSdtIHdvbmRlcmluZyBpZiBpdCBpcyBPSyB0byBkZWNsYXJlIGluY29tcGxldGUgdHlwZSBsaWtl
IHRoaXM6DQoNCi8qIGZvby5oICovDQojaWZuZGVmIEZPT19IDQojZGVmaW5lIEZPT19IDQoNCnR5

Ik koop deze plaat niet; er zit een kras op.

Richard
 
K

Keith Thompson

Ik koop deze plaat niet; er zit een kras op.

"No no, *tobacconist's*!"

Seriously, Mark's article included the following headers:

Content-Type: text/plain; charset="koi8-r"
Content-Transfer-Encoding: base64

My own newsreader was able to display the content as plain text (and I
didn't even notice the encoding until you pointed it out).

Mark, please configure your newsreader so it posts in plain ASCII if
at all possible. Not all newsreaders are able to read base64-encoded
text.
 
K

Keith Thompson

Bingo.

You'd be surprised at how many people have trouble spotting that.

I wouldn't be too surprised, given that most people I know (myself
included) don't speak Dutch, and the Babelfish translation was barely
recognizable.
 
M

Mark

Richard Heathfield said:
Yes, that's fine. In fact, that's a good way to set up opaque types.

Thanks for your reply.
The reason I asked is that I also ran into such declaration of incomplete
type:

/* foo.h */
struct foo;
typedef struct foo *foo_ptr;

/* foo.c */
struct foo {
/* contents go here */
};

So, is it equivalent to what I showed in the original post? If so, then
what's the use of forward structure declaration (I think this is the right
name) ?
 
B

Ben Bacarisse

Mark said:
Thanks for your reply.
The reason I asked is that I also ran into such declaration of
incomplete type:

/* foo.h */
struct foo;
typedef struct foo *foo_ptr;

/* foo.c */
struct foo {
/* contents go here */
};

So, is it equivalent to what I showed in the original post?

In your original (if that is what is quoted) there is no foo_ptr type
and a typedef to make an alias of a struct type so, no, they are not
equivalent.

There is debate over the pros and cons of hiding the "pointerness" of
a type inside a typedef. In general it is probably a bad idea, but
when the typedef is defining a pointer to an opaque type to give a
thing some people think of a "handle", then I would be happy to put the
pointer there in the typedef. Note that I would not then reveal the
"pointerness" through the type's name.

The classic example of this, quite reasonably, takes the other view.
A FILE is an opaque type in that you are not permitted to know
anything much about it so you must always define and use pointer to
FILE objects.
If so,
then what's the use of forward structure declaration (I think this is
the right name) ?

If you mean the "struct foo;" line just before the typedef, then there
is no point in it.
 
L

lawrence.jones

Mark said:
/* foo.h */
struct foo;
typedef struct foo *foo_ptr;

/* foo.c */
struct foo {
/* contents go here */
};

So, is it equivalent to what I showed in the original post? If so, then
what's the use of forward structure declaration (I think this is the right
name) ?

It's almost equivalent. The point of the forward declaration is to
ensure you're talking about a new, incomplete struct type and not some
previously declared struct type. For example, if a user erroneously
declared their own struct foo before including your foo.h, the forward
declaration causes an error. Without it, foo_ptr would simply refer to
the user's struct foo instead of your struct foo.
 
B

Ben Bacarisse

It's almost equivalent. The point of the forward declaration is to
ensure you're talking about a new, incomplete struct type and not some
previously declared struct type. For example, if a user erroneously
declared their own struct foo before including your foo.h, the forward
declaration causes an error. Without it, foo_ptr would simply refer to
the user's struct foo instead of your struct foo.

Interesting. I tried various things before I replied to see if the
lone "struct foo;" had any such effect but I failed to find any (over
and above that provided by the typedef). This included having a
complete struct foo type defined first.

Of course this may simply be a problem with my compiler. It is a
constraint violation or simply UB? I can't find the relevant part of
the standard.
 
T

Tim Rentsch

Ben Bacarisse said:
In your original (if that is what is quoted) there is no foo_ptr type
and a typedef to make an alias of a struct type so, no, they are not
equivalent.

There is debate over the pros and cons of hiding the "pointerness" of
a type inside a typedef. In general it is probably a bad idea, but
when the typedef is defining a pointer to an opaque type to give a
thing some people think of a "handle", then I would be happy to put the
pointer there in the typedef. Note that I would not then reveal the
"pointerness" through the type's name.

The classic example of this, quite reasonably, takes the other view.
A FILE is an opaque type in that you are not permitted to know
anything much about it so you must always define and use pointer to
FILE objects.

A FILE is an object type; it is not opaque in the same way that
a (struct foo *) type, with no definition for the members of
(struct foo), is opaque. The model that is used for FILE
doesn't provide a very good argument for whether to include
pointerness in a typedef for (struct foo*).
 
D

David Thompson

Interesting. I tried various things before I replied to see if the
lone "struct foo;" had any such effect but I failed to find any (over
and above that provided by the typedef). This included having a
complete struct foo type defined first.

Of course this may simply be a problem with my compiler. It is a
constraint violation or simply UB? I can't find the relevant part of
the standard.

I too am skeptical. 6.7.2.3p6 says it declares the tag. p1 constraint
says the _contents_ cannot be redeclared = defined, but not tags.
p3 says 'all declarations' of the same tag in the same scope declare
the same type; that certainly seems to allow more than one, although
it doesn't specifically say a tag-only after the contents -- the
examples are all of the more useful case of tag-only before contents.
6.7p3 constraint says that an identifier [with] no linkage shall not
be redeclared 'except for tags as specified in 6.7.2.3' which punts
the problem right back.

The tag-only form does establish a new type in the shadowing case
(mentioned but not shown in p11 example):

struct foo { one_set_of_stuff };

void bar (void) {
/* inner scope! */
struct foo;
typedef struct foo foo_type;
/* without the tag-only this would use the outer type */
...
struct foo { other_set_of_stuff };
...
}

and it is (formally) needed in the silly prototype case:

struct foo;
void bar (struct foo) ;
/* without the tag-only that would be a separate type,
and the call below formally incorrect */
....
struct foo { stuff } x; ... bar (x); ...

although that can instead use the typedef:

typedef struct foo foo_type;
void bar (foo_type) ;
....
struct foo { stuff } ...

Note: the typedef name can be the same as the tag name,
because they're in separate namespaces, and I often do
make them the same where I use both. But for this example
the distinction is important so I make it more visible.
 

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

No members online now.

Forum statistics

Threads
473,954
Messages
2,570,116
Members
46,704
Latest member
BernadineF

Latest Threads

Top