Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Forward Declarations

11 views
Skip to first unread message

Tom Plunket

unread,
Jun 23, 2006, 5:29:29 PM6/23/06
to
Hey gang;

In C++ I often used forward declarations to allow me to get away with
not including additional files in headers, if in fact all I needed was
to know that a type existed.

e.g.

foo.h:
struct Bar;
void Foo(Bar* b);

...and then foo.cpp would presumably include bar.h, if in fact it
needed to know what Bar consisted of.

How can I accomplish a similar thing in (legal) C?

I've tried the C++ forward-declaration strategy, e.g.

struct Bar;
void Foo(struct Bar* b);

...but I get a compilation failure when struct Bar is actually defined
in a subsequent #include.

I've also tried

void Foo(struct Bar* b);

...all by itself, but that yields a compilation warning about the
definition of struct Bar being only in the scope of this function.

So, can I, and if I can, how can I accomplish what I'm trying to do in
somewhat-pedantic, warning-free C?

thx,
-tom!

Richard Heathfield

unread,
Jun 23, 2006, 5:38:02 PM6/23/06
to
Tom Plunket said:

> Hey gang;
>
> In C++ I often used forward declarations to allow me to get away with
> not including additional files in headers, if in fact all I needed was
> to know that a type existed.
>
> e.g.
>
> foo.h:
> struct Bar;
> void Foo(Bar* b);
>
> ...and then foo.cpp would presumably include bar.h, if in fact it
> needed to know what Bar consisted of.

Quite often, it doesn't need to know, and shouldn't be told. It is perfectly
possible, and indeed commonplace, to put Bar's definition in bar.cpp, so
that only bar.cpp can see it.

>
> How can I accomplish a similar thing in (legal) C?

Just like that, except that it would have to be struct Bar * rather than
just Bar *.

> I've tried the C++ forward-declaration strategy, e.g.
>
> struct Bar;
> void Foo(struct Bar* b);
>
> ...but I get a compilation failure when struct Bar is actually defined
> in a subsequent #include.

Then you did something wrong that we can't see.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)

Tom Plunket

unread,
Jun 23, 2006, 5:53:02 PM6/23/06
to
Richard Heathfield wrote:

>> foo.h:
>> struct Bar;
>> void Foo(Bar* b);
>

>Quite often, it doesn't need to know, and shouldn't be told. It is
>perfectly possible, and indeed commonplace, to put Bar's definition in
>bar.cpp, so that only bar.cpp can see it.

Sure; I do that in other languages frequently enough. I'm sort of a C
newb, though, as 1) I haven't touched it in ten years, and 2) my
exposure to it outside of college was working with people who didn't
endeavor to expand their knowledge of the language. I first
subscribed to comp.lang.c++ in 1996 though, as I needed to learn that
language "for real". :)

>Just like that, except that it would have to be struct Bar * rather
>than just Bar *.

Hmm. Can typedefs come into play here? E.g.

struct Bar;
typedef struct Bar Bar; // this is a common idiom in use here
void Foo(Bar* b);

So if this /should/ be legal, then yeah, I've got some other
challenges in my environment. :)

>Then you did something wrong that we can't see.

Natch; in defense, though, the wrong thing was done long before I
arrived on-scene. :)

Thanks for your help.

-tom!

Richard Heathfield

unread,
Jun 23, 2006, 5:56:50 PM6/23/06
to
Tom Plunket said:

> Richard Heathfield wrote:
>
>>Just like that, except that it would have to be struct Bar * rather
>>than just Bar *.
>
> Hmm. Can typedefs come into play here?

Yes.

<snip>


>
>>Then you did something wrong that we can't see.
>
> Natch; in defense, though, the wrong thing was done long before I
> arrived on-scene. :)

I'd like to help more,
But even clc can't
Make bricks without straw.

Tom Plunket

unread,
Jun 23, 2006, 6:22:25 PM6/23/06
to
Richard Heathfield wrote:

>>>Then you did something wrong that we can't see.
>>
>> Natch; in defense, though, the wrong thing was done long before I
>> arrived on-scene. :)
>
>I'd like to help more,
>But even clc can't
>Make bricks without straw.

Right.

I'll return with straw
When I have found any more
Alas, it's a chore

...and one that brings with it little business value. :)

-tom!

Chris Torek

unread,
Jun 23, 2006, 11:34:55 PM6/23/06
to
[Tom Plunket's original example included the C fragment:]

>>> struct Bar;
>>> void Foo(struct Bar* b);
[which is perfectly OK by itself. Richard Heathfield noted this
and mentioned that if there was a problem, it must be something
else that we cannot see here.]

In article <9coo92dotot68bm3o...@4ax.com>


Tom Plunket <gam...@fancy.org> wrote:
>Hmm. Can typedefs come into play here? E.g.
>
>struct Bar;
>typedef struct Bar Bar; // this is a common idiom in use here
>void Foo(Bar* b);

If the above is in "foo.h", and the following is in "bar.h":

struct Bar;
typedef struct Bar Bar;

then a translation unit that uses this sequence:

#include "foo.h"
#include "bar.h"

expands to the following fragments:

struct Bar;
typedef struct Bar Bar; // and a C99-only comment that \
we hope is not so long that it wraps around and, oh dear, \
good thing each of these lines ends with a backslash-newline
void Foo(Bar* b);


struct Bar;
typedef struct Bar Bar;

This goes wrong because in C (unlike C++) "benign re-typedefs" are
invalid.

There are a number of solutions, but the simplest is to drop the
"typedef" lines entirely and spell out the word "struct" each time.
(One more-complicated one is illustrated by:

struct Bar;
#ifndef BAR_TYPEDEF_DONE
typedef struct Bar Bar;
#define BAR_TYPEDEF_DONE
#endif

This 5-line sequence can appear in multiple header files; it makes
sure that the "typedef" line appears exactly once, no matter how
many of those headers are "#include"d.)
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.

Richard Heathfield

unread,
Jun 24, 2006, 5:47:41 AM6/24/06
to
Chris Torek said:

<snip>
>
> [...] expands to the following fragments:


>
> struct Bar;
> typedef struct Bar Bar; // and a C99-only comment that \
> we hope is not so long that it wraps around and, oh dear, \
> good thing each of these lines ends with a backslash-newline
> void Foo(Bar* b);
> struct Bar;
> typedef struct Bar Bar;
>
> This goes wrong because in C (unlike C++) "benign re-typedefs" are
> invalid.
>
> There are a number of solutions, but the simplest is to drop the
> "typedef" lines entirely and spell out the word "struct" each time.
> (One more-complicated one is illustrated by:
>
> struct Bar;
> #ifndef BAR_TYPEDEF_DONE
> typedef struct Bar Bar;
> #define BAR_TYPEDEF_DONE
> #endif
>
> This 5-line sequence can appear in multiple header files; it makes
> sure that the "typedef" line appears exactly once, no matter how
> many of those headers are "#include"d.)

Better still, he can keep his typedef and avoid the #ifndef by simply
deciding on *one* header, suitably protected by inclusion guards, in which
to place his forward declaration, and then making sure that header is
included by any source that needs it.

Chris Torek

unread,
Jun 24, 2006, 2:32:49 PM6/24/06
to
>Chris Torek said:
>> [...] expands to the following fragments:
[from foo.h:]

>> struct Bar;
>> typedef struct Bar Bar;
>> void Foo(Bar* b);
[and then from bar.h:]

>> struct Bar;
>> typedef struct Bar Bar;
>> This goes wrong because in C (unlike C++) "benign re-typedefs" are
>> invalid.
>>
>> There are a number of solutions, but the simplest is to drop the
>> "typedef" lines entirely and spell out the word "struct" each time.
>> (One more-complicated one is illustrated by:
>>
>> struct Bar;
>> #ifndef BAR_TYPEDEF_DONE
>> typedef struct Bar Bar;
>> #define BAR_TYPEDEF_DONE
>> #endif
>>
>> This 5-line sequence can appear in multiple header files; it makes
>> sure that the "typedef" line appears exactly once, no matter how
>> many of those headers are "#include"d.)

In article <ya-dndQ81dC...@bt.com>,


Richard Heathfield <inv...@invalid.invalid> wrote:
>Better still, he can keep his typedef and avoid the #ifndef by simply
>deciding on *one* header, suitably protected by inclusion guards, in which
>to place his forward declaration, and then making sure that header is
>included by any source that needs it.

This method works, except when it does not. :-)

Consider the following:

struct mutual1 {
struct mutual1 *link;
struct mutual2 *other;
... other data ...
};

struct mutual2 {
struct mutual2 *link;
struct mutual1 *other;
... other data ...
};

Clearly, there is no order in which these mutually-referential data
structures can be defined so that each one is completely defined
before the other one is defined.

For those who insist on treating C as if it were C++, and writing
typedefs for every "struct", the same situation arises when foo.h
declares a function f() that operates on both a "struct foo *" and
a "struct bar *", and bar.h declares a function b() that operates
on both a "struct bar *" and a "struct foo *".

Your challenge is to transform the following valid (and reasonable
enough, albeit hardly complete) C code:

% cat bar.h
struct bar;

struct bar *newbar(int arg1, const char *arg2);

struct foo; /* in foo.h */
void b(struct bar *this, struct foo *other);
/* more stuff snipped */

% cat foo.h
struct foo;

struct foo *newfoo(char *somearg);

struct bar; /* in bar.h */
void f(struct foo *this, struct bar *other);
/* more stuff snipped */

into the kind of pseudo-C++ that so many people seem to favor for
some incomprehensible reason. :-) Here b() must take a "Bar *"
and a "Foo *", and f() must take a "Foo *" and a "Bar *".

Richard Heathfield

unread,
Jun 24, 2006, 3:51:47 PM6/24/06
to
Chris Torek said:

<snip>

> In article <ya-dndQ81dC...@bt.com>,
> Richard Heathfield <inv...@invalid.invalid> wrote:
>>Better still, he can keep his typedef and avoid the #ifndef by simply
>>deciding on *one* header, suitably protected by inclusion guards, in which
>>to place his forward declaration, and then making sure that header is
>>included by any source that needs it.
>
> This method works, except when it does not. :-)

<g> Okay, so what did I miss?

>
> Consider the following:
>
> struct mutual1 {
> struct mutual1 *link;
> struct mutual2 *other;

[and vice versa, so to speak]

Ah yes. I heartily concede that I hadn't considered that. But, in my
defence, I would suggest that the best way to deal with two such
intertwined structures is to handle them both in the same module, in which
case forward declarations for both can be put into the same header.

Tom Plunket

unread,
Jun 26, 2006, 2:41:20 PM6/26/06
to
Chris Torek wrote:

>For those who insist on treating C as if it were C++...

Ah, I didn't realize what a sin it was to use 'typedef'. Why'd they
put it in the standard do you figure?

Anyway, thanks for your comments; it's helpful to find the useful
information that exists therein.

As it is, I'm faced with a codebase that has many features which are
undesirable. The sad catch-22 is that I don't know what many of the
problems are 'til I ask about them, and then get these little snipes
along with the response. Hey, yeah, C99 allows the '//' comment. Oh
my, intelligent adults will be unable to figure out how to deal with
multiline comments! The end is nigh! Terr'ists surround us! ...that
all besides the fact that when I was a wee lad the C compiler I used
supported C++-style single-sine comments, and one tends to forget what
features are in which standard when one uses more than one language.
-tom!

Keith Thompson

unread,
Jun 26, 2006, 4:00:14 PM6/26/06
to
Tom Plunket <gam...@fancy.org> writes:
> Chris Torek wrote:
>>For those who insist on treating C as if it were C++...
>
> Ah, I didn't realize what a sin it was to use 'typedef'. Why'd they
> put it in the standard do you figure?

C has no concept of "sin" other than the trigonometric function. 8-)}

The idea that using typedefs for structure types is poor style is
actually rather controversial. Chris Torek and I happen to agree on
this point, but plenty of smart people don't.

An argument in favor of using a typedef is that it provides a one-word
name for a type. In my opinion, that's not a strong enough reason to
use it.

But typedefs are useful in other contexts. I ilke them for declaring
complex pointer-to-function types, and the standard library defines
typedefs for a number of integer type (size_t, int32_t, etc.).

> Anyway, thanks for your comments; it's helpful to find the useful
> information that exists therein.
>
> As it is, I'm faced with a codebase that has many features which are
> undesirable. The sad catch-22 is that I don't know what many of the
> problems are 'til I ask about them, and then get these little snipes
> along with the response. Hey, yeah, C99 allows the '//' comment. Oh
> my, intelligent adults will be unable to figure out how to deal with
> multiline comments! The end is nigh! Terr'ists surround us! ...that
> all besides the fact that when I was a wee lad the C compiler I used
> supported C++-style single-sine comments, and one tends to forget what
> features are in which standard when one uses more than one language.

Yes, we do tend to snipe. We deal with a *lot* of inexperienced C
programmers (some of whom have good questions but may not know the
best way to ask them) and, unfortunately, with a fair number of
deliberate trolls. At times our patience may wear a bit thin. We
are, of course, only human, and we make mistakes (I made a doozy here
just the other day). Ignore the snipes and you'll learn a lot here.

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.

Tom Plunket

unread,
Jun 26, 2006, 6:01:00 PM6/26/06
to
Keith Thompson wrote:

>> Ah, I didn't realize what a sin it was to use 'typedef'. Why'd they
>> put it in the standard do you figure?
>
>C has no concept of "sin" other than the trigonometric function. 8-)}

Ah very good. ;)

>The idea that using typedefs for structure types is poor style is
>actually rather controversial. Chris Torek and I happen to agree on
>this point, but plenty of smart people don't.

Ok then. ;)

It's been years since I "learned" C, but at the time the sage wisdom
who was providing me with guidance said I should /always/ typedef
structures, because a structure definition all by itself would yield
an unnamed instance of that structure right at that location. E.g.

struct something
{
int member;
};

This would deposit an unnamed "struct something" right there in the
translation unit (if that's the proper C vocabulary, anyway) where the
structure was defined. In these days of GHz processors an GB of RAM
that "problem" would certainly be less of an issue, and dead-stripping
linkers would also take care of it for you, but I don't feel too far
out on a limb by thinking that The Almighty Standard says nothing of
giga-anything nor of linking.

>Yes, we do tend to snipe. We deal with a *lot* of inexperienced C
>programmers (some of whom have good questions but may not know the
>best way to ask them) and, unfortunately, with a fair number of
>deliberate trolls.

Kill filters set on "save my sanity". ;) Oof, they're not in The
Standard, either.

> Ignore the snipes and you'll learn a lot here.

Heh, with any luck we'll have this project rolled over to C++ in no
time at all. The gun's a lot more complicated but at least I
understand how it works. :)

thx,
-tom!

Richard Heathfield

unread,
Jun 26, 2006, 6:06:25 PM6/26/06
to
Tom Plunket said:

> Chris Torek wrote:
>
>>For those who insist on treating C as if it were C++...
>
> Ah, I didn't realize what a sin it was to use 'typedef'. Why'd they
> put it in the standard do you figure?

It's not a sin at all. It's a blessing from dmr himself, and we should be
grateful for it, and use it when it's appropriate to do so. Incidentally,
as someone else has already pointed out, the real sin is in the math
library. As it says in the Bible somewhere, don't cast stone[0] if you've
included <math.h>.

There are some smart people who think using typedef is a bad idea. I'm not
among them. Does that mean I'm not smart? Well, maybe it does. But I think
there's a lot of sense behind having a one-word name for a type. But I
would not go so far as to hide a pointer in a typedef - not even for
function pointers.

> As it is, I'm faced with a codebase that has many features which are
> undesirable. The sad catch-22 is that I don't know what many of the
> problems are 'til I ask about them, and then get these little snipes
> along with the response.

You're allowed to treat those with a pinch of salt if they're undeserved.

> Hey, yeah, C99 allows the '//' comment.

That's nice. When C99 becomes as portable as C90 is now, it may even become
relevant. :-)

> Oh
> my, intelligent adults will be unable to figure out how to deal with
> multiline comments! The end is nigh! Terr'ists surround us! ...that
> all besides the fact that when I was a wee lad the C compiler I used
> supported C++-style single-sine comments,

Two coughs here. Firstly, single-sine? I thought we'd agreed it was sin!

Secondly, they are actually BCPL-style comments, and dmr took the
(presumably) deliberate (and, in my opinion, wise) decision to *omit* them
from C. It is true that bs chose to include them into C++, but he did not
invent them.

> and one tends to forget what
> features are in which standard when one uses more than one language.

Certainly true, especially when the syntaxes (syntaces? syntaxii?) are
ostensibly similar in so many ways.

Keith Thompson

unread,
Jun 26, 2006, 8:52:49 PM6/26/06
to
Tom Plunket <gam...@fancy.org> writes:
[...]

> It's been years since I "learned" C, but at the time the sage wisdom
> who was providing me with guidance said I should /always/ typedef
> structures, because a structure definition all by itself would yield
> an unnamed instance of that structure right at that location. E.g.
>
> struct something
> {
> int member;
> };
>
> This would deposit an unnamed "struct something" right there in the
> translation unit (if that's the proper C vocabulary, anyway) where the
> structure was defined. In these days of GHz processors an GB of RAM
> that "problem" would certainly be less of an issue, and dead-stripping
> linkers would also take care of it for you, but I don't feel too far
> out on a limb by thinking that The Almighty Standard says nothing of
> giga-anything nor of linking.

Hmm, I've never heard that argument. A struct declaration like the
one above does not create an anonymous object. (Even if it did, since
there would be no way to refer to it, any compiler worth its salt
would optimize it away.)

Logically, since this:

struct something {
int member;
} x, y;

creates two objects, and this:

struct something {
int member;
} x;

creates one object, then this:

struct something {
int member;
};

should create none.

Frederick Gotham

unread,
Jun 26, 2006, 9:55:30 PM6/26/06
to
Keith Thompson posted:


> Logically, since this:
>
> struct something {
> int member;
> } x, y;
>
> creates two objects, and this:
>
> struct something {
> int member;
> } x;
>
> creates one object, then this:
>
> struct something {
> int member;
> };
>
> should create none.


In the spirit of keeping things mathematical, maybe "should create zero
objects" would be a better way of putting it? ; )


--

Frederick Gotham

Frederick Gotham

unread,
Jun 26, 2006, 10:03:03 PM6/26/06
to
Tom Plunket posted:


> struct something
> {
> int member;
> };
>
> This would deposit an unnamed "struct something" right there in the
> translation unit (if that's the proper C vocabulary, anyway) where the
> structure was defined.


Common sense tells me that that would be a whoefully redundant thing to do.

Even in C++ -- where the definition of an object can result in the
execution of code -- it would still be ridiculous (C++ is full of all sorts
of optimizations like "Named Return Value Optimization").


--

Frederick Gotham

Keith Thompson

unread,
Jun 26, 2006, 10:19:12 PM6/26/06
to

In the spirit of reading and writing plain English, I thought (and
still think) that the word "none" is perfectly clear.

Dave Thompson

unread,
Jul 10, 2006, 4:19:33 AM7/10/06
to
On Mon, 26 Jun 2006 15:01:00 -0700, Tom Plunket <gam...@fancy.org>
wrote:

<snip>


> It's been years since I "learned" C, but at the time the sage wisdom
> who was providing me with guidance said I should /always/ typedef
> structures, because a structure definition all by itself would yield
> an unnamed instance of that structure right at that location. E.g.
>
> struct something
> {
> int member;
> };
>

Does not, and never did, as already said.

Are you sure you, or your guide, didn't misunderstand or misremember
the issue of including or omitting a struct _tag_?

struct { elements } zorg;
creates an 'anonymous' struct type, which you cannot refer to later --
i.e. you cannot create another variable, or a pointer to one, or a
function argument, etc., which the compiler will recognize as the same
type. (Unlike some other non-C languages, which do use so called
'structural' or 'deep' type compatibility.)

struct good { elements } lelu;
creates a variable _and_ a tag (type) which you can use later.
struct good { elements };
creates _only_ the tag (type). Which you can use later,
and that is the only way to use it, so if you don't, it's useless :-)

<OT> In C++, the tag name(s) in the latter form are available as
typenames by themselves, in addition to 'struct good' as in C (which
is still permitted). People often say loosely that C++ automatically
does typedef for you, but this isn't strictly right; even in C++ you
can still declare both an ordinary-identifier foo and a tag foo in the
same scope, but it is very bad style, even worse than it is in C.

- David.Thompson1 at worldnet.att.net

ena8...@yahoo.com

unread,
Jul 17, 2006, 4:57:45 PM7/17/06
to

Keith Thompson wrote:
> Tom Plunket <gam...@fancy.org> writes:
> > Chris Torek wrote:
> >>For those who insist on treating C as if it were C++...
> >
> > Ah, I didn't realize what a sin it was to use 'typedef'. Why'd they
> > put it in the standard do you figure?
>
> C has no concept of "sin" other than the trigonometric function. 8-)}
>
> The idea that using typedefs for structure types is poor style is
> actually rather controversial. Chris Torek and I happen to agree on
> this point, but plenty of smart people don't.
>
> An argument in favor of using a typedef is that it provides a one-word
> name for a type. In my opinion, that's not a strong enough reason to
> use it.

Usually the best argument in favor of using a typedef
is that the name defined reflects how you want client
code to view the type.

If client code should view the type as opaque, then
using typedef for a struct type makes sense.

If client code should view a struct transparently,
then no typedef - just use struct.

In my programming, I tend to use abstraction rather
a lot, so usually types are typedef'ed. But I
understand both modes, and use either, as appropriate.

There is one typedef idiom that bears mentioning:

typedef struct { ... } *Foo;

This idiom is useful when the underlying structs
should always be malloc'ed, never static or auto.
Because of how the type is declared, no code will
have these structs other than {m,c,re}alloc()'ed
ones.

Keith Thompson

unread,
Jul 17, 2006, 5:46:40 PM7/17/06
to

In that case, it would probably make sense to hide the *alloc() and
free() calls behind some type-specific functions; the *alloc()
wrappers could also do any necessary initialization of the struct
members. This is basically what fopen() and fclose() do, except that
the pointer isn't hidden behind a typedef; the members of the
structure are effectively hidden not by the fact that there's no name
for the structure type (there is, FILE), but by the fact that the
details are undocumented, and code that depends on them will break
when ported.

ena8...@yahoo.com

unread,
Jul 18, 2006, 5:53:24 AM7/18/06
to

It depends what you want to achieve.

If you want the structure members accessed only
within the implementing module, then something
like

typedef struct __FILE FILE;

in a header, and a subsequent

struct __FILE { ... };

in the implementing module is usually a good way
to do that. (User code shouldn't use the __'s,
other than that it's the same.)

If what you want is to guarantee that struct
instances are made only through *alloc(), then
the idiom

typedef struct { ... } *Foo;

is a way to do that. C gives you a choice,
but it has to be one or the other, it can't
be both.

Michael Wojcik

unread,
Jul 18, 2006, 4:13:04 PM7/18/06
to

In article <1153169865....@35g2000cwc.googlegroups.com>, ena8...@yahoo.com writes:
>
> Usually the best argument in favor of using a typedef
> is that the name defined reflects how you want client
> code to view the type.
>
> If client code should view the type as opaque, then
> using typedef for a struct type makes sense.

Perhaps, but since C already contains an idiom for opaque types which
does not require a typedef, the typedef is still superfluous.

When I have a header file that contains:

struct foo;
struct foo *MakeFoo(void);
void UseFoo(struct foo *);

I know I am dealing with an opaque type. No typedef need apply.

What's more, I know that I am dealing with a pointer-to-incomplete-
structure, which is useful if, for example, I want to insert some
temporary instrumentation that tracks the struct-foo objects I have
created; I know I can print some representation of a struct foo *
with the %p format specifier (after casting it to void *), for
example. That's much better than a typedef that hides the fact that
I am dealing with a pointer, which I realize is not what you
suggested, but is all too common among the fans of typedef.

--
Michael Wojcik michael...@microfocus.com

Not the author (with K.Ravichandran and T.Rick Fletcher) of "Mode specific
chemistry of HS + N{_2}O(n,1,0) using stimulated Raman excitation".

ena8...@yahoo.com

unread,
Jul 19, 2006, 5:59:08 PM7/19/06
to

Michael Wojcik wrote:
> In article <1153169865....@35g2000cwc.googlegroups.com>, ena8...@yahoo.com writes:
> >
> > Usually the best argument in favor of using a typedef
> > is that the name defined reflects how you want client
> > code to view the type.
> >
> > If client code should view the type as opaque, then
> > using typedef for a struct type makes sense.
>
> Perhaps, but since C already contains an idiom for opaque types which
> does not require a typedef, the typedef is still superfluous.
>
> When I have a header file that contains:
>
> struct foo;
> struct foo *MakeFoo(void);
> void UseFoo(struct foo *);
>
> I know I am dealing with an opaque type. No typedef need apply.

You're using the term opaque in different
sense than I was. As I was using the term,
opaque means a type that client code should
know nothing about. The "opaque type" that
you suggest requires client code to know
(a) that it's a pointer and (b) that the
pointer points to a struct. That's more
translucent than opaque.

For translucent types, struct foo * is ok.
A type intended to be really opaque should
use typedef.

Michael Wojcik

unread,
Jul 20, 2006, 5:43:45 PM7/20/06
to

In article <1153346348.6...@i3g2000cwc.googlegroups.com>, ena8...@yahoo.com writes:
> Michael Wojcik wrote:
> > In article <1153169865....@35g2000cwc.googlegroups.com>, ena8...@yahoo.com writes:
> > >
> > > If client code should view the type as opaque, then
> > > using typedef for a struct type makes sense.
> >
> > Perhaps, but since C already contains an idiom for opaque types which
> > does not require a typedef, the typedef is still superfluous.
>
> You're using the term opaque in different
> sense than I was.

Perhaps I am, but I find the distinction you draw trivial and
superfluous.

> As I was using the term,
> opaque means a type that client code should
> know nothing about.

This cannot be achieved in C. The "client code" can still apply the
sizeof operator to a typedef, for example. Assuming an implementation
with decent QoI, trial code can be written to determine what types the
typedef is compatible with, by observing diagnostics.

> The "opaque type" that
> you suggest requires client code to know
> (a) that it's a pointer and (b) that the
> pointer points to a struct.

So what? In some languages (eg OCaml), essentially all user-defined
types are references to structured types. That doesn't seem to be a
problem for them. I'll be damned if I can think why this would be a
problem in C. What precious information is leaking out of the abstract
interface?

> That's more translucent than opaque.

The size and nature of the contents are invisible outside the
interface. That's as good a definition of "opaque type" as any other
I've seen.

> A type intended to be really opaque should
> use typedef.

You're welcome to that opinion. I don't find it convincing.

I'll also note in passing that using typedefs for "opaque" (abstract)
types encourages not wrapping elementary types in structs. And that,
in turn, may lead to maintenance problems, if the type needs to be
extended later, but existing callers have divined its non-struct
nature (eg by reading headers) and written code that relies on that
fact.

If struct is your only type-abstraction mechanism, this is never an
issue.

--
Michael Wojcik michael...@microfocus.com

He smiled and let his gaze fall to hers, so that her cheek began to
glow. Ecstatically she waited until his mouth slowly neared her own.
She knew only one thing: rdoeniadtrgove niardgoverdgovnrdgog.

ena8...@yahoo.com

unread,
Jul 22, 2006, 11:58:17 AM7/22/06
to

Michael Wojcik wrote:
> In article <1153346348.6...@i3g2000cwc.googlegroups.com>, ena8...@yahoo.com writes:
> > Michael Wojcik wrote:
> > > In article <1153169865....@35g2000cwc.googlegroups.com>, ena8...@yahoo.com writes:
> > > >
> > > > If client code should view the type as opaque, then
> > > > using typedef for a struct type makes sense.
> > >
> > > Perhaps, but since C already contains an idiom for opaque types which
> > > does not require a typedef, the typedef is still superfluous.
> >
> > You're using the term opaque in different
> > sense than I was.
>
> Perhaps I am, but I find the distinction you draw trivial and
> superfluous.

As long as you understand the distinction, you can respond
to what I'm actually saying and not something else.

> > As I was using the term,
> > opaque means a type that client code should
> > know nothing about.
>
> This cannot be achieved in C. The "client code" can still apply the
> sizeof operator to a typedef, for example. Assuming an implementation
> with decent QoI, trial code can be written to determine what types the
> typedef is compatible with, by observing diagnostics.

Would you prefer "a type that client code should know as
little about as possible"?

In any case that's not really the issue. The question is
not whether client code _can_ know something, but whether it
_must_ know something. An opaque type is one where
knowledge of the type's representation should be confined to
the module that supplies/implements it.

> > The "opaque type" that
> > you suggest requires client code to know
> > (a) that it's a pointer and (b) that the
> > pointer points to a struct.
>
> So what? In some languages (eg OCaml), essentially all user-defined
> types are references to structured types. That doesn't seem to be a
> problem for them. I'll be damned if I can think why this would be a
> problem in C. What precious information is leaking out of the abstract
> interface?

If the type is defined with a typedef, essentially no
representation information has to leak out.

If there isn't a typedef, then representation information is
forced to leak out, as explained above.

> > That's more translucent than opaque.
>
> The size and nature of the contents are invisible outside the
> interface. That's as good a definition of "opaque type" as any other
> I've seen.

You seem to want to argue about what makes a good definition
for "opaque type". Either you're missing the point of what
I'm saying, or you're just being deliberately difficult.

> > A type intended to be really opaque should
> > use typedef.
>
> You're welcome to that opinion. I don't find it convincing.

I expect that's because you insist on reading "opaque" in
the sense you want to use it rather than as I have been
using it.

> I'll also note in passing that using typedefs for "opaque" (abstract)
> types encourages not wrapping elementary types in structs. And that,
> in turn, may lead to maintenance problems, if the type needs to be
> extended later, but existing callers have divined its non-struct
> nature (eg by reading headers) and written code that relies on that
> fact.
>
> If struct is your only type-abstraction mechanism, this is never an
> issue.

Your reasoning here starts with a false premise.

Chris Torek

unread,
Jul 30, 2006, 4:13:14 PM7/30/06
to
[on opaque types, and "typedef" vs "struct"]

In article <1153583897.2...@s13g2000cwa.googlegroups.com>


<ena8...@yahoo.com> wrote:
>In any case that's not really the issue. The question is
>not whether client code _can_ know something, but whether it
>_must_ know something. An opaque type is one where
>knowledge of the type's representation should be confined to
>the module that supplies/implements it.

Indeed -- but the problem with "typedef" is that, in C, you must
know whether the typedef-name is a synonym for an array type,
because array types do not behave the same as any other type.

Hence, typedef is not at all useful for opaque types in C, unless
you also add a (programmer-imposed) constraint that no typedef ever
names an array type.

If you simply make every opaque type a structure -- no pointer is
required -- you get the desired effect:

struct user_type_0;
struct user_type_1;
struct user_type_2;

All three types are unique and well-behaved, and the client code
need not know anything at all. If we "peek behind the curtain",
we might find, e.g.:

struct user_type_0 { double val; };
struct user_type_1 { double val; };
struct user_type_2 { struct u2_implementation *ptr; };

but all the user knows about them is that each one behaves like
other primitive types in C (of course, one can never apply arithmetic
operators directly, for instance).

ena8...@yahoo.com

unread,
Jul 31, 2006, 1:32:01 AM7/31/06
to

Your comments don't make sense to me. If I'm defining
an opaque type that needs an array representation, the
type can be defined as an array wrapped in a struct:

typedef struct { whatever_t v[SOME_N]; } opaque;

If I'm defining an opaque type that shouldn't be assigned
or have its value passed directly, but always by address,
the type can be defined wrapping the representation type
in an array:

typedef whatever_t opaque[1];
void assign_opaque( opaque *, opaque * );

...

opaque o1, o2;
assign_opaque( &o1, &o2 ); /* can't do o1 = o2; */

If I'm defining an opaque type that needs to have
some scalar operations exposed (eg, == 0), the type
can be defined in a usual way:

typedef struct some_struct_t *opaque;

...

opaque o1;
if(!o1) ...

If I'm defining an opaque type that's a scalar type
but should _not_ have scalar operations exposed, the
type can be wrapped in a struct:

typedef struct { scalar_t v; } opaque;

And finally, if I'm defining an opaque type that's
a scalar type that shouldn't be assigned and also
shouldn't be accidentally turned into a pointer to
its scalar representation, the type can be wrapped
in struct wrapped in an array:

typedef struct { scalar_t v; } opaque[1];

In all of these cases the client code uses just the
name that was typedef'ed. Using typedef is more
flexible than always using struct, not less flexible.

Chris Torek

unread,
Jul 31, 2006, 2:58:26 AM7/31/06
to
In article <1154323921.2...@i42g2000cwa.googlegroups.com>,

<ena8...@yahoo.com> wrote:
>f I'm defining an opaque type that shouldn't be assigned
>or have its value passed directly, but always by address,
>the type can be defined wrapping the representation type
>in an array:
>
> typedef whatever_t opaque[1];
> void assign_opaque( opaque *, opaque * );

This argument did occur to me later today (while at the gym),
and is one in favor of typedef with nothing *but* arrays, since
using any other type allows ordinary assignment:

T a, b;
...
b = a; /* error if T is an alias for an array type */

which is in fact letting information (namely, "simple assigmnent
is OK") leak out of the abstraction. But C confounds this, at
least to some extent:

void f(T a, T b) {
...
b = a; /* oops, succeeds after all */
...
}

through the "array rewritten as pointer" rule for parameters.

(This is the sort of problem I mean to indict, regarding arrays:
their "array-ness" leaks out in ways one does not always intend.
Again, this particular problem can be avoided through programmer
discipline; but if you are going to call upon discipline, you can
simply dispense with opaque types in the first place. :-) )

(As a side comment, a lot of my instincts here were set back in
the Bad Old Days when "&array" was diagnosed, instead of producing
a pointer to the entire array.)

ena8...@yahoo.com

unread,
Aug 1, 2006, 10:19:25 AM8/1/06
to

Chris Torek wrote:
> In article <1154323921.2...@i42g2000cwa.googlegroups.com>,
> <ena8...@yahoo.com> wrote:
> >f I'm defining an opaque type that shouldn't be assigned
> >or have its value passed directly, but always by address,
> >the type can be defined wrapping the representation type
> >in an array:
> >
> > typedef whatever_t opaque[1];
> > void assign_opaque( opaque *, opaque * );
>
> This argument did occur to me later today (while at the gym),
> and is one in favor of typedef with nothing *but* arrays, since
> using any other type allows ordinary assignment:
>
> T a, b;
> ...
> b = a; /* error if T is an alias for an array type */
>
> which is in fact letting information (namely, "simple assigmnent
> is OK") leak out of the abstraction. But C confounds this, at
> least to some extent:
>
> void f(T a, T b) {
> ...
> b = a; /* oops, succeeds after all */
> ...
> }
>
> through the "array rewritten as pointer" rule for parameters.

Excellent point. No good way around this problem that I can
see.

> (This is the sort of problem I mean to indict, regarding arrays:
> their "array-ness" leaks out in ways one does not always intend.
> Again, this particular problem can be avoided through programmer
> discipline; but if you are going to call upon discipline, you can
> simply dispense with opaque types in the first place. :-) )

I don't think of it as an either/or situation. Many (most?)
programming techniques benefit from both discipline and some
level of automated support. A combination of the two is
usually better than either individually.

Furthermore, much of the motivation (as I see it) for using
opaque types is to protect against a change in representation.
If a typedef is used in such cases, then it's easier to switch
between the different scenarios given in my previous message.
Using some struct type as the declaring type limits some options.
Of course, sometimes limiting those options is just what you
want, and in cases like that using struct is a better expression
of the intent. In most cases though I usually want to preserve
as much representation flexibility as I can.

> (As a side comment, a lot of my instincts here were set back in
> the Bad Old Days when "&array" was diagnosed, instead of producing
> a pointer to the entire array.)

Of course I'm guessing, but I conjecture that a difference
in our respective backgrounds could account for a lot of
the difference in our viewpoints. I was already very well
grounded in abstract data types and information hiding by
the time I started programming in C; using typedef to define
abstract/opaque types is something I did probably literally
on day one of my C programming experience.

None of which is meant to say anything negative about anyone with
a different background. I enjoy and benefit from the comments
that come out of your experience and background.

0 new messages