advantage of using typedefs

J

junky_fellow

I was looking at the source code of linux or open BSD. What I found
that lots of typedefs
were used. For example, consider the offset in a file. It was declared
as
off_t offset;
and off_t is typedefed as
typedef long off_t;

I wanted to know what advantage do we get by typedefs ? Why we did not
declare
offset simply as
long off_t;

Similar is the case with size of file and so on.....

Does any one has any clue of why do we use typedefs in such cases ?

Thanx for any help advance ....
 
E

Emmanuel Delahaye

(e-mail address removed) a écrit :
I wanted to know what advantage do we get by typedefs ? Why we did not
declare
offset simply as
long off_t;

It's often a portability issue. Imagine you want a type that hold the
biggest possible unsigned integer.

You will have this in C90:

typedef unsigned long biggest_int_t;

and that on C99:

typedef unsigned long long biggest_int_t;

With the help of some smart macros and #ifxxx directives, After a simple
recompile, the type will automagically be corrected after a simple
recompile of the project/lib or whatever.
 
J

junky_fellow

Emmanuel said:
(e-mail address removed) a écrit :

It's often a portability issue. Imagine you want a type that hold the
biggest possible unsigned integer.

You will have this in C90:

typedef unsigned long biggest_int_t;

and that on C99:

typedef unsigned long long biggest_int_t;

With the help of some smart macros and #ifxxx directives, After a simple
recompile, the type will automagically be corrected after a simple
recompile of the project/lib or whatever.

Thanx for your reply, Emmanuel. But if this is the case (ie we need
the biggest possible
unsigned integer to hold offset ) then why not declare
unsigned long long offset;
I believe unsigned long long should be highest possible integer type ?
Or am I wrong ?

Is there any other reason for doing that ?
 
M

Mark McIntyre

On 28 Jan 2006 00:50:55 -0800, in comp.lang.c ,

(concerning typdef'ing)
Thanx for your reply, Emmanuel. But if this is the case (ie we need
the biggest possible unsigned integer to hold offset ) then why not declare
unsigned long long offset;

Because then its size would vary from platform to platform, and on a
c90 implementation it would be an error. Some implementations spell
"long long" as "__int64", others don't support it at all etc etc.
I believe unsigned long long should be highest possible integer type ?
Or am I wrong ?

You're right, but the size of long long varies from platform to
platform
Mark McIntyre
 
W

William J. Leary Jr.

Thad Smith said:
That is one good reason. I often define types for another: I create my
own conceptual data type, which I document where the typedef occurs.
For example:

typedef unsigned long ticks; /* elapsed time, unit = 16 us. */
...
ticks pulseWidth; /* width of last pulse */
ticks pulseInterval; /* time between last 2 pulses */
void DelayTicks (ticks delay);

Here using ticks carries with it the type of data and the scaling.

From a tools perspective, this can be quite powerful. I've picked up
development on a project where the former developer used typedefs just as
you've done above. One of the advantages is that when I encounter one of his
typedef'd items, I can do a right-click on it and one of the menu options is
"show typedef" which, when selected, shows the typedef, in context. Thus, I
can immediately tell what he meant the variable or argument to hold or
represent. Very handy, and saves a pile of time. Even in the older system
where I have to find them using grep, it still means that once I've found it,
the comments on what it means are right there.

- Bill
 
T

Thad Smith

That is one good reason. I often define types for another: I create my
own conceptual data type, which I document where the typedef occurs.
For example:

typedef unsigned long ticks; /* elapsed time, unit = 16 us. */
....
ticks pulseWidth; /* width of last pulse */
ticks pulseInterval; /* time between last 2 pulses */
void DelayTicks (ticks delay);

Here using ticks carries with it the type of data and the scaling.
 
K

Keith Thompson

Mark McIntyre said:
On 28 Jan 2006 00:50:55 -0800, in comp.lang.c ,

(concerning typdef'ing)


Because then its size would vary from platform to platform, and on a
c90 implementation it would be an error. Some implementations spell
"long long" as "__int64", others don't support it at all etc etc.


You're right, but the size of long long varies from platform to
platform

Yes, but if you want the biggest unsigned integer type, you have to
accept that its size is going to vary.

If you want a type whose size *doesn't* vary, you want
something like uint64_t (and if you need this in C90, see
<http://www.lysator.liu.se/c/q8/>). (I think most C90 implementations
these days provide 64-bit integer types under some name.)

BTW, C99 defines uintmax_t for precisely this purpose.
 
K

Keith Thompson

Mark McIntyre said:
... which was my point.

Then I'm confused. junky_fellow's question was:

But if this is the case (ie we need the biggest possible unsigned
integer to hold offset ) then why not declare unsigned long long
offset

and you replied:

Because then its size would vary from platform to platform, and on
a c90 implementation it would be an error.

The second point is valid (except for the many C90 implementations
that provide "long long" as an extension), but I don't see what you're
getting at with the first point. If you want the biggest possible
unsigned integer, its size *will* vary from platform to platform, but
you presented the variation in size as a reason not to use "unsigned
long long".

(And C99 allows extended integer types, so unsigned long long isn't
necessarily the longest unsigned integer type. unsigned long long
could be 64 bits, and uintmax_t could be 128 bits.)

Either you want unsigned long long (whose size will vary, and which
may not exist in a C90 implementation), or you want the largest
unsigned integer type (whose size will vary), or you want an unsigned
type that's exactly 64 bits (which probably exists, but it's not
guaranteed in either C90 or C99). For the second and third cases, a
typedef is reasonable -- and C99's <stdint.h> provides uintmax_t and
uint64_t, respectively. For the first case, if you specifically want
unsigned long long for some reason, a typedef would be silly. Mark,
without going back to the question of what point you were making
upthread, is there anything in this paragraph you disagree with?
 
J

Jordan Abel

I was looking at the source code of linux or open BSD. What I found
that lots of typedefs
were used. For example, consider the offset in a file. It was declared
as
off_t offset;
and off_t is typedefed as
typedef long off_t;

On your system. On my system it's long long.
 
J

Jordan Abel

Then I'm confused. junky_fellow's question was:

But if this is the case (ie we need the biggest possible unsigned
integer to hold offset ) then why not declare unsigned long long
offset

and you replied:

Because then its size would vary from platform to platform, and on
a c90 implementation it would be an error.

Regardless, there's a factual problem. The "offset" (assuming it is
posix off_t) is used in places where it must be negative, and thus
should not be unsigned anything. the original post said long, not
unsigned long.
 
C

Chris Torek

I wanted to know what advantage do we get by typedefs?

In many ways, none at all.

In one somewhat important way, a fairly big one.

The underlying problem / solution here is "abstraction".

Fundamentally, abstraction is all about removing unnecessary
detail, while retaining necessary detail. (Not all abstractions
*succeed* at this task, mind.) This is really the heart of
most (maybe even all) computer programming.

The real world is ineluctably complex. Computer programs tend to
deal with simplified models. Even so, the simplified models are
often (maybe even usually) sufficiently complicated that programmers
cannot debug them without breaking them down even further. This
is why we use higher level languages -- it is at least one reason,
and I think one can argue that it is really the only reason, that
we do not write everything in machine code -- and also why we
break large programs into functions and (in langauges like C)
data structures.

C has a number of mostly-concrete data types: char, short, int,
and long (and in C99 long long) and their unsigned variants; float,
double, and long double; and pointers. (C99 adds complex number
types. C also has "void", but this is mostly a degenerate case,
and "enum", but enum is just a special case of "integral".) These
types are already somewhat abstracted from the underlying machine;
for instance, many microprocessors do not have hardware support
(or full support) for 64 or even 32 bit integral types, and many
only offer floating-point as an option (with a coprocessor) and/or
need software assistance. Still, these types are generally "close
to the metal", as the phrase goes: most CPUs implement most of them
mostly in hardware (although complex numbers rarely have direct
hardware support).

In addition to these (and derived types -- which actually include
all the pointer types as well as arrays; note also that "pointer
to function returning T" is a data type, even though it points to
a function type), C has the "user-defined abstract data type",
which is spelled "struct". Whenever you declare or define a new
struct, you get a new type. This new type is not compatible with
any existing type:

struct one { int val; } a;
struct two { int val; } b;
...
a = b; /* ERROR, type mismatch */

User-defined types give you everything you need to make new,
compiler-checked abstractions. A subroutine or function is also
an abstraction, but unless you make it use or return a user-defined
data type, it is possible to apply it to data of an inappropriate
type. Consider, for instance, a subroutine that checks a
temperature, which you might use in the control system for a
nuclear reactor:

void check_temperature(double the_value) { ... }

If you are only checking "any old double" (as in this case), it is
possible to call this with a length or pressure measurement by
accident:

double x;
...
x = measure_something(...); /* actually returns a pressure */
...
check_temperature(x);

A C compiler is not required to complain, and it would be surprising
to find one that does. Make separate "temperature" and "pressure"
data types, however, and we get:

void check_temperature(struct temperature the_value) { ... }
...
struct pressure x;
...
x = measure_something(...);
...
check_temperature(x);

Now the compiler *is* required to complain.

The problem with C's "typedef" is that it does *not* actually define
types. Instead, it just defines an alias for some existing type.
The aliases can be mixed freely. Thus if we try to use:

typedef double temperature;
typedef double pressure;

we lose many of the advantages of abstract data types.

At the same time, however, typedefs *do* give us a level of
indirection. Consider ANSI/ISO C "size_t", for instance. The
Standard tells us that size_t is an unsigned integral type that
holds the size (in bytes, which C calls "char"s) of an object. In
general, size_t is an alias for one of three types: unsigned int,
unsigned long, or unsigned long long. (Technically it could be an
alias for unsigned char or unsigned short, or even one of the weird
"extra" types allowed in C99, but in practice this does not occur.)

This "level of indirection" acts as a sort of "leaky" abstract
type. It fails to provide compile-time type-checking; we can use
the wrong types all over the place and never even notice. But *if*
we manage to use it correctly, it does insulate us from any changes
needed when porting code from one machine to another. If size_t
should be "unsigned int" on one 64-bit machine, but "unsigned long"
on another, it *can* be. We can -- if we are sufficiently careful
-- avoid assuming that it is either one or the other.

The two things typedef gives you, that struct does not, are:

- you do not need to write out the word "struct" each time, and

- you can make the type-name a synonym for a fundamental, built
in type (like "long" or "unsigned char"), rather than a
user-defined abstract type.

In C89 (but not C99), that second point is significant, because
there is no way in C89 to write constants of user-defined type.
In C99 we can do things like this:

struct pressure { double val; };
#define PRESSURE_UNKNOWN ((struct pressure){-1})
...
struct pressure p = estimate_pressure(...);
...
some_loop_construct {
...
if (some_condition)
p = PRESSURE_UNKNOWN; /* make sure we measure it below */
...
if (p == PRESSURE_UNKNOWN) ...
...
}

(Of course, in C89 you can always write macros to deal with
this, e.g., #define SET_TO_UNKNOWN(pp) ((pp)->val = -1), but
this is hardly elegant.)

Structure values are often implemented relatively inefficiently.
If this is the case for any particular situation / compiler, we
can use typedef to get a "checked system", and then recompile to
get a (presumably faster) "unchecked" version:

#ifdef SLEAZE
typedef double pressure;
#define MK_CONSTANT(t, val) ((t)(val))
#else
typedef struct pressure pressure;
struct pressure { double val; };
#define MK_CONSTANT(t, val) ((t){val})
#endif
...
pressure estimate_pressure(...);
#define PRESSURE_UNKNOWN MK_CONSTANT(pressure, -1)

Now we simply need to "#define SLEAZE" to get the "unchecked"
version. (The MK_CONSTANT trick above is again C99-dependent.)
 
M

Mark McIntyre

Then I'm confused. junky_fellow's question was:

But if this is the case (ie we need the biggest possible unsigned
integer to hold offset ) then why not declare unsigned long long
offset

and you replied:

Because then its size would vary from platform to platform, and on
a c90 implementation it would be an error.
but I don't see what you're
getting at with the first point. If you want the biggest possible
unsigned integer, its size *will* vary from platform to platform, but
you presented the variation in size as a reason not to use "unsigned
long long".

My point was that it will vary, and is thus a nonportable construct in
terms of say file access. If all you care about is one platform, then
no problem.
But this thread was about why one would use typedefs, and one reason
is to keep such platform specific stuff in a single place.
Mark McIntyre
 
K

Keith Thompson

Mark McIntyre said:
My point was that it will vary, and is thus a nonportable construct in
terms of say file access. If all you care about is one platform, then
no problem.
But this thread was about why one would use typedefs, and one reason
is to keep such platform specific stuff in a single place.

Of course it will vary. junky_fellow *specifically* asked about "the
biggest possible unsigned integer". Maybe that's not the best thing
to use for a file offset -- and if that was your point, you might have
said so.

But *if* you need the biggest possible unsigned integer, for whatever
reason, it's perfectly sensible to use a typedef for it (or, in C99,
to use the existing uintmax_t). There could be any number of reasons
you'd want that. Of course, any code using it would have to allow for
the fact that the size will vary from one platform to another.

I notice that you snipped the direct question that I asked you.
 
C

CBFalconer

Keith said:
.... snip ...

I notice that you snipped the direct question that I asked you.

As a general principle I suggest that snipping should be done in
units of paragraphs. This reduces the alteration of meaning by
quoting out of context. This might also have the desirable side
effect of encouraging reasonable paragraphing.

--
"The power of the Executive to cast a man into prison without
formulating any charge known to the law, and particularly to
deny him the judgement of his peers, is in the highest degree
odious and is the foundation of all totalitarian government
whether Nazi or Communist." -- W. Churchill, Nov 21, 1943
 
R

Rod Pemberton

I was looking at the source code of linux or open BSD. What I found
that lots of typedefs
were used. For example, consider the offset in a file. It was declared
as
off_t offset;
and off_t is typedefed as
typedef long off_t;

I wanted to know what advantage do we get by typedefs ? Why we did not
declare
offset simply as
long off_t;

Similar is the case with size of file and so on.....

Does any one has any clue of why do we use typedefs in such cases ?

Thanx for any help advance ....


In response to:
I wanted to know what advantage do we get by typedefs ?

I always seem to have problems with structures when they aren't typedef'd.
When structures are typedef'd, you can cast values to structure elements and
take the size of structure elements without actually having a valid,
allocated, and filled data structure. For example, say you have three
structures which should be nested but are separate. Now, you need some data
from the third structure. But, you have a raw buffer that corresponds to
the first structure, and, of course, contains the data for second and third
structures. How do you get to the data in the third structure? Using
typedef'd structures and casts, you can access the data from the third
structure in the raw buffer without allocating space for or filling in the
structures. You only need to allocate space for the raw buffer and
variables which are assigned data from the third structure. You have total
access to any data in the raw buffer, but you don't need to allocate and
fill any intermediate structures.


Rod Pemberton
 
K

Keith Thompson

Rod Pemberton said:
I wanted to know what advantage do we get by typedefs ?
[...]
I always seem to have problems with structures when they aren't typedef'd.
When structures are typedef'd, you can cast values to structure elements and
take the size of structure elements without actually having a valid,
allocated, and filled data structure. For example, say you have three
structures which should be nested but are separate. Now, you need some data
from the third structure. But, you have a raw buffer that corresponds to
the first structure, and, of course, contains the data for second and third
structures. How do you get to the data in the third structure? Using
typedef'd structures and casts, you can access the data from the third
structure in the raw buffer without allocating space for or filling in the
structures. You only need to allocate space for the raw buffer and
variables which are assigned data from the third structure. You have total
access to any data in the raw buffer, but you don't need to allocate and
fill any intermediate structures.

Given:

typedef struct foo_s {
/* member declarations */
} foo_t;

I can't think of anything you can do with "foo_t" that you can't just
as easily do with "struct foo_s" -- which is why I prefer the simpler:

struct foo {
/* member declarations */
};

Can you provide an example where the typedef gives you a real
advantage? (Saving keystrokes doesn't count as a "real advantage".)

I'm not sure I understand your example anyway; the use of casts always
makes me nervous. If you have "three structures which should be
nested but are separate", what's stopping you from nesting them?
But in any case, a cast to "struct foo*" is as valid as a cast to
"foo_t*".
 
K

Keith Thompson

CBFalconer said:
As a general principle I suggest that snipping should be done in
units of paragraphs. This reduces the alteration of meaning by
quoting out of context. This might also have the desirable side
effect of encouraging reasonable paragraphing.

I think the snipping was done in units of paragraphs. The question
was part of an entire paragraph that was snipped. (Not that he's
under any obligation to answer any particular question, of course.)
 
R

Rod Pemberton

Keith Thompson said:
Rod Pemberton said:
I wanted to know what advantage do we get by typedefs ?
[...]
I always seem to have problems with structures when they aren't typedef'd.
When structures are typedef'd, you can cast values to structure elements and
take the size of structure elements without actually having a valid,
allocated, and filled data structure. For example, say you have three
structures which should be nested but are separate. Now, you need some data
from the third structure. But, you have a raw buffer that corresponds to
the first structure, and, of course, contains the data for second and third
structures. How do you get to the data in the third structure? Using
typedef'd structures and casts, you can access the data from the third
structure in the raw buffer without allocating space for or filling in the
structures. You only need to allocate space for the raw buffer and
variables which are assigned data from the third structure. You have total
access to any data in the raw buffer, but you don't need to allocate and
fill any intermediate structures.

Given:

typedef struct foo_s {
/* member declarations */
} foo_t;

I can't think of anything you can do with "foo_t" that you can't just
as easily do with "struct foo_s" -- which is why I prefer the simpler:

struct foo {
/* member declarations */
};

Can you provide an example where the typedef gives you a real
advantage? (Saving keystrokes doesn't count as a "real advantage".)

I'm not sure I understand your example anyway; the use of casts always
makes me nervous. If you have "three structures which should be
nested but are separate", what's stopping you from nesting them?
But in any case, a cast to "struct foo*" is as valid as a cast to
"foo_t*".
San Diego Supercomputer Center <*>
We must do something. This is something. Therefore, we must do this.

It could be used anywhere nested structures are used, such as a database.
But, let's work with two hard disk structures: 'partition boot sector' and
'bios parameter block' (which is part of the 'partition boot sector'). Say
you have a buffer, 'buf[512]', of unsigned char for reading sectors off the
hard disk. Now, you've read the partition boot sector off the hard disk
into your buffer. How do you get access data in the 'bios parameter block'
by name instead of by 'buf[x]'? You cast the buffer to a 'partition boot
sector'. From the 'partition boot sector', you get the offset of the 'bios
parameter block' which is then cast to a 'bios parameter block'. From the
casted 'bios parameter block', you get the data you need. For example, to
get the number of heads (FAT32X) (this is from working code) :

unsigned char buf[512];
//some disk read routine which gets the partition boot sector, not the
master boot sector
num_heads=((bios_parameter_block *)&(((partition_boot_sector
*)&buf)->bpb))->number_of_heads;


This is same as the following sequence of 'pseudo' code. We take the
address of buf:
'&buf'

which is then cast as a 'partition boot sector':
'(partition_boot_sector *)&buf'

Now that we have a 'partition boot sector', we locate the 'bios parameter
block':
'partition_boot_sector->bpb'

We then cast the address of the 'bios parameter block' as a 'bios parameter
block':
'(bios_parameter_block *)&(partition_boot_sector->bpb)'
This address should be something like &buf+12 or buf[12].

Now that we have a 'bios parameter block', we locate the number of heads:
'bios_parameter_block->number_of_heads'
This address of this data should be something lik &buf+12+16 or buf[28].

(The structs are big so I'll direct you to Ralph Brown's Interrupt list for
their layout.)

As long as the layout of the structures are correct (i.e., packed, if
necessary), you can get the data you want without dealing with any offsets
or copying data to/from structures etc.


Hope that helps.

Rod Pemberton
 
K

Keith Thompson

Rod Pemberton said:
Keith Thompson said:
Rod Pemberton said:
news:[email protected]... [...]
I wanted to know what advantage do we get by typedefs ? [...]
I always seem to have problems with structures when they aren't
typedef'd. When structures are typedef'd, you can cast values to
structure elements and take the size of structure elements
without actually having a valid, allocated, and filled data
structure. For example, say you have three structures which
should be nested but are separate. Now, you need some data from
the third structure. But, you have a raw buffer that corresponds
to the first structure, and, of course, contains the data for
second and third structures. How do you get to the data in the
third structure? Using typedef'd structures and casts, you can
access the data from the third structure in the raw buffer
without allocating space for or filling in the structures. You
only need to allocate space for the raw buffer and variables
which are assigned data from the third structure. You have total
access to any data in the raw buffer, but you don't need to
allocate and fill any intermediate structures.

Given:

typedef struct foo_s {
/* member declarations */
} foo_t;

I can't think of anything you can do with "foo_t" that you can't just
as easily do with "struct foo_s" -- which is why I prefer the simpler:

struct foo {
/* member declarations */
};

Can you provide an example where the typedef gives you a real
advantage? (Saving keystrokes doesn't count as a "real advantage".)

(Please don't quote signatures.)

[snip]
unsigned char buf[512];
//some disk read routine which gets the partition boot sector, not the
master boot sector
num_heads=((bios_parameter_block *)&(((partition_boot_sector
*)&buf)->bpb))->number_of_heads;


This is same as the following sequence of 'pseudo' code. We take the
address of buf:
'&buf'

which is then cast as a 'partition boot sector':
'(partition_boot_sector *)&buf'

Now that we have a 'partition boot sector', we locate the 'bios parameter
block':
'partition_boot_sector->bpb'

We then cast the address of the 'bios parameter block' as a 'bios parameter
block':
'(bios_parameter_block *)&(partition_boot_sector->bpb)'
This address should be something like &buf+12 or buf[12].

Now that we have a 'bios parameter block', we locate the number of heads:
'bios_parameter_block->number_of_heads'
This address of this data should be something lik &buf+12+16 or buf[28]. [snip]
Hope that helps.

Um, not really.

It's hard to tell without seeing the actual code, but I presume
bios_parameter_block and partition_boot_sector are typedefs for some
struct types. Let's assume they're called "struct bios_parameter_block"
and "struct partition_boot_sector".

Then your statement

num_heads=((bios_parameter_block *)&(((partition_boot_sector *)&buf)->bpb))->number_of_heads;

could as easily be written

num_heads=((struct bios_parameter_block *)&(((struct partition_boot_sector *)&buf)->bpb))->number_of_heads;

As I said, there's nothing you can do with typedefs for struct types
that you can't do just as easily with the struct types themselves.
 

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,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top