Re: advantage of using typedefs



In article <1138434930.477944.161090@xxxxxxxxxxxxxxxxxxxxxxxxxxxx>
junky_fellow@xxxxxxxxxxx <junky_fellow@xxxxxxxxxxx> wrote:
>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.)
--
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.
.



Relevant Pages

  • Re: "extern struct foobar" linux compilation warning
    ... >from an int to a structure or vice-versa. ... Sure -- but then one could just define a struct containing a single ... function requiring a "pressure". ... You can add a typedef if you ...
    (comp.lang.c)
  • Re: Process dump facility public API - pdpublic.h
    ... struct _PDOPTIONS *pSystemDefaults; /* Ptr to System Defaults struct */ ... typedef DDPREQUEST *PDDREQUEST; ... also be specified in the DDPREQUEST flags. ... /* PDUNION is used in both the PDPROCESS and PDPROCESS2 structures. ...
    (comp.os.os2.bugs)
  • Re: syntax errror
    ... >>> int maxGrey' ... >> declaration of maxGrey. ... typedef double; ... struct some_tag_name { ...
    (comp.lang.c)
  • Re: question about struct??
    ... It is not the "typedef" keyword that defines the type. ... is the "struct {" pair that does it: ... struct S {int a; int b;}; ...
    (comp.lang.c)
  • Re: structs help
    ... >same name and the second compiler didn't like it. ... As I like to point out, the real problem with typedef is that it ... This is even more pronounced in the "typedef struct ..." ... time, though, I expect any programmer working on the zorgle program ...
    (comp.lang.c)

Quantcast