Forward Declarations

T

Tom Plunket

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!
 
R

Richard Heathfield

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.
 
T

Tom Plunket

Richard said:
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!
 
R

Richard Heathfield

Tom Plunket said:
Hmm. Can typedefs come into play here?
Yes.



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.
 
T

Tom Plunket

Richard said:
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!
 
C

Chris Torek

[Tom Plunket's original example included the C fragment:][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.]

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

Richard Heathfield

Chris Torek said:

[...] 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.
 
C

Chris Torek

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

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 *".
 
R

Richard Heathfield

Chris Torek said:

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

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.
 
T

Tom Plunket

Chris said:
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!
 
K

Keith Thompson

Tom Plunket said:
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. :cool:}

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.
 
T

Tom Plunket

Keith said:
C has no concept of "sin" other than the trigonometric function. :cool:}

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!
 
R

Richard Heathfield

Tom Plunket said:
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.
 
K

Keith Thompson

Tom Plunket said:
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.
 
F

Frederick Gotham

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? ; )
 
F

Frederick Gotham

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").
 
K

Keith Thompson

Frederick Gotham said:
Keith Thompson posted:

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

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

Dave Thompson

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
 
E

ena8t8si

Keith said:
C has no concept of "sin" other than the trigonometric function. :cool:}

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.
 
K

Keith Thompson

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.

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.
 

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,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top