Could you explain this typedef to me?

J

Jorgen Grahn

But tagless structs cannot be forward declared without typedefs either.

The concept of typedef'ing a struct isn't to not have tags existing, but
to not need to continually use the "struct" keyword.

True, but what I tend to see in code which typedefs everything is that
they either omit the tag, or pick an ugly name. If they have a rule
"typedef name is tag name" then I don't mind so much.
The typedef also
allows a bit of information hiding, the type could be a struct, or a
union, or an enum, or a fundamental type.
Much of the code may just
treat it as an opaque type that is passed from here to there, and sent
to/from functions as needed.

Yes, but then you often want a forward declaration -- if you don't need
the full struct definition and don't want to needlessly pull in header
files.

/Jorgen
 
E

Eric Sosman

[...]
Much of the code may just
treat it as an opaque type that is passed from here to there, and sent
to/from functions as needed.

Yes, but then you often want a forward declaration -- if you don't need
the full struct definition and don't want to needlessly pull in header
files.

I don't understand. If you're not using "the full struct
definition" and you're not treating it as an opaque type, just
what are you doing? And, er, how?
 
T

Tim Rentsch

Jorgen Grahn said:
For once I would have been happy to be proven wrong ...

What I often want to do in practice is to deal with 'struct foo*'
from some library, without having to pull in the library's header
file. I like every translation unit to know as little as
possible -- see as few names as possible.

It seems like your complaint is not with using typedef names for
structs but with badly done header files. Good design practice
would be to provide a separate header file just for the types of
the module, for exactly the reason you state. Anyone who has
looked through the tangled mess in /usr/include show understand
this.

A similar comment applies to structs defined without tags. If it
is useful to be able to name a particular struct type without
making use of its content (eg, in 'struct foo*'), then a forward
declaration should be provided separably from its content. That
may entail one additional higher strata of header file, for the
forward declarations, if it is also important to access the type
definitions separately from the modules data/function definitions.
(Obviously there are other workable schemes besides having two or
three separate header files; the important thing is a capability
for getting just type definitions or declarations without all the
other baggage.)

The trick of using 'struct foo*' without #include'ing its header
(<editorial>a questionable practice</editorial>) obviously won't
work with with typedef'ed tagless structs, but when there are tags
things are looking up: the new rule in C11 allows a typedef like

typedef struct foo_s Foo;

to be repeated in a translation unit, which would allow you to
forward declare a typedef'ed struct even if subsequently the
module's header were #include'ed.
 
J

Jorgen Grahn

[...]
Much of the code may just
treat it as an opaque type that is passed from here to there, and sent
to/from functions as needed.

Yes, but then you often want a forward declaration -- if you don't need
the full struct definition and don't want to needlessly pull in header
files.

I don't understand. If you're not using "the full struct
definition" and you're not treating it as an opaque type, just
what are you doing? And, er, how?

I'm treating it as an opaque type -- surely I never said I didn't?

I am a bit puzzled by this thread, because I initially thought it was
for the benefit of newbies -- that I only explained something simple
that all C programmers would know and practice already. But now I'm
not so sure.

Let's take a somewhat concrete example. If I write foo.h:

/* foo.h */
struct bar_lib;

struct Foo {
struct bar_lib* impl;
// ...
};

struct Foo foo_create(void);
void do_something(struct Foo*);

foo.h exposes an interface to some functionality, and I chose to use
the "bar" library to implement it. The foo.h clients can ignore the
implementation details. Also, I don't want to force these clients to
pull in barlib.h because it's big and "dirty".

It's my impression that I cannot accomplish this unless I know the
struct tag "bar_lib". A typedef won't do: it may be opaque, but I
cannot use it without pulling in some bar_lib.h header.

If I misunderstand this I would love to learn about it!

/Jorgen
 
E

Eric Sosman

]
Much of the code may just
treat it as an opaque type that is passed from here to there, and sent
to/from functions as needed.

Yes, but then you often want a forward declaration -- if you don't need
the full struct definition and don't want to needlessly pull in header
files.

I don't understand. If you're not using "the full struct
definition" and you're not treating it as an opaque type, just
what are you doing? And, er, how?

I'm treating it as an opaque type -- surely I never said I didn't?

Apparently not. It seems I read more into what you wrote
than you put there. (In American usage, when Speaker A says
"X" and Speaker B responds "Yes, but Y," it usually means that
Y is an objection to X, that B thinks X is wrong in some way.
I misunderstood you as denying that the type was opaque, but it
appears you didn't mean that at all. Sorry!)
I am a bit puzzled by this thread, because I initially thought it was
for the benefit of newbies -- that I only explained something simple
that all C programmers would know and practice already. But now I'm
not so sure.

Let's take a somewhat concrete example. If I write foo.h:

/* foo.h */
struct bar_lib;

Not sure why this is needed ...
struct Foo {
struct bar_lib* impl;
// ...
};

struct Foo foo_create(void);
void do_something(struct Foo*);

foo.h exposes an interface to some functionality, and I chose to use
the "bar" library to implement it. The foo.h clients can ignore the
implementation details. Also, I don't want to force these clients to
pull in barlib.h because it's big and "dirty".

It's my impression that I cannot accomplish this unless I know the
struct tag "bar_lib". A typedef won't do: it may be opaque, but I
cannot use it without pulling in some bar_lib.h header.

If I misunderstand this I would love to learn about it!

Is the example realistic? Perhaps so -- Different programmers
face different demands and address them in different styles, but
this isn't a style I think I'd choose. Why expose the fact that
foo depends on bar? That is, why reveal the `struct bar_lib*'
nature of `impl' to a user of "foo.h"? Considerations:

- If the user is to use `impl' to get at the innards of the
`struct bar_lib', he'll need to include "bar_lib.h" anyhow and all
this name-hiding is for naught. Even if `struct bar_lib' remains
opaque but the user knows he can use the `impl' element to call
bar_lib's functions he'll need "bar_lib.h" for their declarations.

- If the user treats `struct bar_lib' as opaque *and* doesn't
call any bar_lib functions directly (but always goes through foo),
then `impl' can be of any struct pointer type at all (because all
struct pointers smell the same), although this might lead to a lot
of tedious cast operators in the implementation of foo. `impl'
could even be a `void*', at the expense of a tiny amount of type
safety.

- When opacity is one of the goals, I usually find it better
to keep `struct Foo' opaque and have my foo_create() function return
a pointer to a newly-allocated `struct Foo' rather than a completed
instance thereof. That way I need reveal nothing whatsoever about
bar_lib, and am free to adopt the improved xbar_lib without disturbing
foo's clients.

Finally, I still don't see what this has to do with "forward
declarations" or with typedefs -- the string `typedef' is entirely
absent from the example ...
 
J

Jorgen Grahn

On 2/6/2014 2:16 PM, Jorgen Grahn wrote:
]
Much of the code may just
treat it as an opaque type that is passed from here to there, and sent
to/from functions as needed.

Yes, but then you often want a forward declaration -- if you don't need
the full struct definition and don't want to needlessly pull in header
files.

I don't understand. If you're not using "the full struct
definition" and you're not treating it as an opaque type, just
what are you doing? And, er, how?

I'm treating it as an opaque type -- surely I never said I didn't?

Apparently not. It seems I read more into what you wrote
than you put there. (In American usage, when Speaker A says
"X" and Speaker B responds "Yes, but Y," it usually means that
Y is an objection to X, that B thinks X is wrong in some way.
I misunderstood you as denying that the type was opaque, but it
appears you didn't mean that at all. Sorry!)

Ah, I can see that kind of interpretation now. I should probably have
written "Yes. But then ...".
Not sure why this is needed ...

Seems it's not. Must be some habit I picked up; I don't know when or
where. Based on the warnings gcc shows me, it's needed in a related
situation:

struct bar_lib;
void foo(struct bar_lib*);
Is the example realistic? Perhaps so -- Different programmers
face different demands and address them in different styles, but
this isn't a style I think I'd choose.

It's the usual lesson of c.l.c -- you think you only do what
"everyone" does, and then it turns out they don't.

I should probably admit here that my primary language is C++. It might
be the case that this style is more common there. I didn't bring it
up because it seemed the languages worked the same in this area
(except you can refer to 'struct Foo' as just 'Foo').
Why expose the fact that
foo depends on bar? That is, why reveal the `struct bar_lib*'
nature of `impl' to a user of "foo.h"? Considerations:

- If the user is to use `impl' to get at the innards of the
`struct bar_lib', he'll need to include "bar_lib.h" anyhow and all
this name-hiding is for naught. Even if `struct bar_lib' remains
opaque but the user knows he can use the `impl' element to call
bar_lib's functions he'll need "bar_lib.h" for their declarations.

True. So this isn't the reason. If all users of foo.h will want
bar_lib.h too, the forwarding above is pointless. The assumption above
is that users will want to deal with Foo objects, but probably not
mess with the struct bar_lib.

'impl' was a badly chosen name -- it makes it sound as if struct Foo
is just a wrapper for a struct bar_lib. I think it's more realistic
to assume struct Foo contains a bunch of immediately useful elements,
/and/ this opaque handle.

By the way, I now realize a much clearer and simpler example would
have been something like this:

// serialize.h
struct in_addr;
struct in6_addr;
struct Foo;
int serialize_addr(char* buf, size_t len, struct in_addr val);
int serialize_addr6(char* buf, size_t len, struct in6_addr val);
int serialize_foo(char* buf, size_t len, struct Foo val);
// ...
- If the user treats `struct bar_lib' as opaque *and* doesn't
call any bar_lib functions directly (but always goes through foo),
then `impl' can be of any struct pointer type at all (because all
struct pointers smell the same), although this might lead to a lot
of tedious cast operators in the implementation of foo. `impl'
could even be a `void*', at the expense of a tiny amount of type
safety.

Yes, although I value my type safety and try to stay clear of void*.
- When opacity is one of the goals, I usually find it better
to keep `struct Foo' opaque and have my foo_create() function return
a pointer to a newly-allocated `struct Foo' rather than a completed
instance thereof. That way I need reveal nothing whatsoever about
bar_lib, and am free to adopt the improved xbar_lib without disturbing
foo's clients.

This is why I shouldn't have used the name 'impl' in the example. Yes,
if the contents of struct Foo are entirely uninteresting to its
clients, I would probably make it opaque, too.
Finally, I still don't see what this has to do with "forward
declarations" or with typedefs -- the string `typedef' is entirely
absent from the example ...

Yes, absent it is. I should have made the purpose of the example more
clear. My question was "could I have done this without knowing the
struct tag bar_lib?" and "how would knowing a typedef for it have
helped me?"

/Jorgen
 

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

Latest Threads

Top