Typedef names vs. new types.

Karl Heuer karl at haddock.ima.isc.com
Tue Oct 25 14:55:10 AEST 1988


In article <3355 at pt.cs.cmu.edu> dwp at k.gp.cs.cmu.edu (Doug Philips) writes:
>I have found that it can be very useful to use typedef to create a conceptual
>entity (this is probably an abuse of the mechanism).  The problem with this
>(as mentioned before) is that the compiler doesn't really consider this a new
>type.  Even lint is, at first glance, no help. ...

This issue comes up every now and then, and each time it does, I threaten to
post my ideas on the subject, but I keep holding off because they're not
finished.  Well, some feedback from the net might make it easier, so I'm
posting what I've got.

disclaim {
    The ideas described below are incomplete.  I already know that.
    Criticism is okay, but let's keep it constructive.

    Don't followup to tell me how to do it in C++.  I already know that,
    too, and I don't think it's what I'm looking for.  There ought to be a
    way to do this without having to define a new class and operator set
    for each conceptually different type, and for each combination of them
    that I might want.

    I agree that the syntax used below is poor.  Once we have the
    semantics nailed down, we can pretty it up.

    This probably isn't the right newsgroup (I'm certainly not proposing
    that this change for ANSI C), but this is where the most recent
    discussion has been taking place.
}


Many people have noted that typedef only creates a synonym for an existing
type, so that after creating types `foo' and `bar' (both typedef'd to int) it
is still legal to write `f1 = b1'.  Let's try to design a consistent language
that would detect this, yet still be useful.

First question: is `f1 = 1' legal?  How about `f1 + i' (where `i' is int)?
Someone suggested (last time this topic came up) that the new types should be
intermixable with the original, but not with each other.  Presumably this
means that the int is converted to a foo, and that this is the type of the
result.

I don't agree that this would be useful behavior, at least for my programs.
It would produce two incorrect warnings for `dist = vel * time', and silently
allow the (dimensionally incorrect) `time = i / time'.  Why bother to apply a
half-solution to the special case of mixed assignment if it isn't going to
behave properly for arithmetic?

A related problem is that of enums.  ANSI C makes them ints in disguise,
because nothing else would do The Right Thing.  Clearly one would like to be
able to distinguish enum from int, but it should also be possible to use an
enum as the subscript of an array.  (In fact, it should *not* be legal to use
a plain int if the array is expecting to be subscripted by the enum!)


Currently, a C expression has a type and a value.  Let us postulate a stricter
language, called CDA, in which an expression has a type, a value, and a
dimension.  The dimension of all existing C arithmetic types is "Scalar".
Each pointer type has its own additive dimension (see below for definition).

(I envision this as "a new language" only in the same sense that the language
acceptable to lint is different from that of cc.  In fact, I would imagine a
lint-like processor which recognizes a new set of pragmas embedded in what cc
would interpret as comments or as macros with empty expansion.  In the
examples below, assume `#define _CDA(ignore)' and you'll get the normal C
syntax; the macro's argument is visible only to the CDA program.)

Let's enhance the syntax of declarations to include a dimension-specifier
(defaulting to Scalar) in addition to a type-specifier (defaulting, as usual,
to int).

There are two types of nonscalar dimensions: Additive and Multiplicative.  An
additive dimension A satisfies the rule { Scalar + A --> A } and the other two
rules implied by this (viz. { A + Scalar --> A } and { A - A --> Scalar }),
whereas a multiplicative dimension M satisfies { Scalar * M --> M } (etc.),
and also { M + M --> M }.


Example 1: Define types "length_t" and "area_t" with dimensional information.
  typedef _CDA(new_m_dim()) double length_t;
  typedef _CDA(length_t * length_t) double area_t;
  length_t x, y, z;
  area_t   surface = 2*(x*y + y*z + x*z);    /* ok */
  foo(x*y*z);
The expression x*y*z is acceptable and has dimensions of "cubic length_t" even
though no type has been declared yet to have such dimensions.  When "foo" is
defined it must expect an argument of such a type:
  void foo(_CDA(length_t * length_t * length_t) double v) { ... }
Note that it is not necessary to define a "volume_t".  (Neither was "area_t"
necessary; "_CDA(length_t * length_t) double" would have been equivalent.)

Example 2: Temperature conversions.
  typedef _CDA(new_m_dim()) double Kelvin;
  typedef _CDA(new_m_dim()) double Rankine;
  typedef _CDA(new_a_dim() * Kelvin) double Centigrade;
  typedef _CDA(new_a_dim() * Rankine) double Fahrenheit;
  #define MULTIPLIER ((_CDA(Rankine / Kelvin) double)1.8)
  #define ADDER ((_CDA(Fahrenheit-Centigrade*Rankine/Kelvin) double)32)
  Fahrenheit ctof(Centigrade c) { return (c * MULTIPLIER) + ADDER; }

Example 3: Make an array to be subscripted by enum.
  typedef _CDA(new_a_dim()) enum { RED, BLUE, GREEN } color;
  #define NCOLORS 3
  typedef long  array_by_int[NCOLORS];
  typedef _CDA(-color+) array_by_int array_by_color;
  typedef long *pointer_into_array_by_int;
  typedef _CDA(-color+) pointer_into_array_by_int pointer_into_array_by_color;
  array_by_color a;
  pointer_into_array_by_color p;
  long *q;
  p = &a[0];      /* ok; same as p = a */
  q = &a[RED];    /* ok; same value as p, but not identical */
  p[BLUE] = *q++; /* ok; p may be subscripted by a color, q by a scalar */
  p += BLUE-RED;  /* ok; BLUE-RED is a scalar int */
  *p = 0L;        /* illegal: p does not have pointer dimensions */
  q[GREEN] = 0L;  /* illegal: q+GREEN does not have pointer dimensions */
  a[0] = 0L;      /* illegal: a+0 does not have pointer dimensions */
  if (q-p == RED) /* ok */


That's as far as I've gotten...

Karl W. Z. Heuer (ima!haddock!karl or karl at haddock.isc.com), The Walking Lint
Reread the disclaimers before you followup.



More information about the Comp.std.c mailing list