Topic: A type safe typedef?


Author: rinn@apopka.cs.ucf.edu (Jason Rinn)
Date: 23 Feb 1994 18:20:13 -0500
Raw View
js@aron (Jerzy Sobczyk) writes:

>Russell Gold (russgold@access.netaxs.com) wrote:
>: Ronald F. Guilmette (rfg@netcom.com) wrote:
>: : In article <1830@racerx.bridge.COM> rodb@bridge.com (Rod Burman) writes:
>: : >
>: : >    I was trying to think of a simple method to get
>: : >a "type safe typdef", i.e. one that was not just a nick
>: : >name for the original type but also conferred type safety...

Actually, a look at the windows.h file for windows 3.1 shows an interesting
technique that works in SOME circumstances.  Use a struct with only one member.
The probelm of course is that you can't just add two structs together.  But it
does provide strong type safety.  It might do what you want.

Later,
Jason





Author: kelley@debbie.fbsw.tt.com (Kevin Kelly)
Date: Fri, 18 Feb 1994 15:34:34 GMT
Raw View
rodb@bridge.com (Rod Burman) writes:

>Bill Barrington (bbarring@batph104.NoSubdomain.NoDomain) wrote:

>: In article <1830@racerx.bridge.COM>, rodb@bridge.com (Rod Burman) writes:
>: |> class roman : public int {}
>-- much removed to save space

>:  Yeah, I think it would be really cool if we could redefine the
>: operations for all builtin types.  That probably wouldn't cause too many
>: problems, would it?


Well, if you want to be able to derive from, and add to the functionality of,
built-in types, there's always the method that Stroustrup mentioned:

class Int {
 private:
    int i;
 public:
    Int(int ii=0) : i(ii) {}
    operator int() { return i; }
};

Now you can use Int's wherever an int would be accepted, and you can derive
from Int and override whatever you want.

More generally:

template<class T> class Derivable {
    T value;
 public:
    Derivable( T const &t=0 ) : value( t ) { }
    operator T() const { return value; }
};

. . .
typedef Derivable<int>            Int;
typedef Derivable<unsigned int>   UInt;
typedef Derivable<long>           Long;
. . .


In some cases, this can be pretty useful.

Kevin Kelley

--
        **    +                 '.          -<>-            o
      ******      O       O      ,'     _              o
      ******                     .    '(_).                      +
        **            O         ',              kelley@merlin.hood.edu




Author: robert@mirage.visix.com (Robert Mollitor)
Date: Fri, 18 Feb 1994 20:33:25 GMT
Raw View
In article <1994Feb16.173724.24133@bnr.ca> bbarring@batph104.NoSubdomain.NoDomain (Bill Barrington) writes:
>
>   In article <1830@racerx.bridge.COM>, rodb@bridge.com (Rod Burman) writes:
>   |>
>   |>     I was trying to think of a simple method to get
>   |> a "type safe typdef", i.e. one that was not just a nick
>   |> name for the original type but also conferred type safety, but
>   |> without all the hassle of re-writing the class this would be
>   |> useful for classes with a lot of members for which you wish
>   |> to change a few (0,1..) behaviours but not the vast majority
>   |> and I cmae up with the following method (I've based the example
>   |> on roman numerals, but actually was thinking of an error reporting
>   |> class when I first had the idea, I'm sure there are other uses..)
>   |>
>   |> #include <iostream.h>
>   |> class roman : public int {}
>   |>
>   |> ostream &operator<<(ostream& os, roman r) {
>   |> {
>   |>     for (int i = r; i >= 1000; i -= 1000) {
>   |>         os << 'M';
>   |>     }
>   |> // further code elided for brevity
>   |>     return os;
>   |> }
>   |>  int main() {
>   |>      os << "Hello world (copyright " << roman(1970) << " K&R)" << endl;
>   |> }
>   |>
>
>    Yeah, I think it would be really cool if we could redefine the
>   operations for all builtin types.  That probably wouldn't cause too many
>   problems, would it?

No one mentioned redefining operators for builtin types.  Rather, it would
be nice to redefine operators for types that happen to be implemented as
builtin integral or floating-point types.

>    Seriously, the class construct provides what you are looking for
>   (i.e., type safety), for user defined types.

class maybe_short_or_char
{
#if (want_short)
    short c;
#else
    char c;
#endif
}

would be fine if sizeof(maybe_short_or_char[N]) == sizeof(char[N]) in
the absense of want_short.  However, it sort of defeats the purpose if
it does not.

>    If you have a copy of the latest Journal of Object Oriented
>   Programming, look at the article by Andrew Koenig.  This is related to what
>   you are trying to achieve.  He discusses the rationale
>   for providing a new type for the C++ language (a boolean type).  A primary
>   motivator was the ability to provide behavior appropriate for a boolean
>   type that can only be achieved by making it a true type.  In other words,
>   if boolean was defined to be, say "typedef int boolean", then outputting
>   this to a stream can only result in doing what is appropriate for an int.
>   If, however, there were a true boolean type, then the stream could do
>   whatever is appropriate for a boolean.

(I haven't read the article.)  This is fine for "boolean" (and "wchar_t" ?),
but does the language have to be extended for every _t type?

>    Ints, floats, char*s, doubles, etc. are NOT "builtin classes" of
>   the C++ language.  There is NO way to "add to" or otherwise change their
>   behavior.

For me at least, the question is not about changing builtin behavior, but
about arbitrating overloads, and maybe type safety.

(In the spirit of giving a poor anachronistic keyword a home:)

#if (want_short)
  overload typedef short maybe_short_or_char;
#else
  overload typedef char  maybe_short_or_char;
#endif

would allow "vfoo::vfoo(maybe_short_or_char x)" to not interfere with
"vfoo::vfoo(short x)" in the presence of want_short.  Plus a
maybe_short_or_char would implicitly cast to a short or char, but
the reverse would need to be done explicitly. (But I haven't really
thought about this part too much.)


robt

(P.S., I know some C++ people hate the preprocessor.)

--
Robert Mollitor            robert@visix.com
Visix Software Inc.     ...!uupsi!visix!robert




Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Sun, 20 Feb 1994 20:20:44 GMT
Raw View
In article <1830@racerx.bridge.COM> rodb@bridge.com (Rod Burman) writes:
>
>    I was trying to think of a simple method to get
>a "type safe typdef", i.e. one that was not just a nick
>name for the original type but also conferred type safety...

Sort of a `typedef++', eh? :-)

But seriously, someone else already noted (here, I believe) that such a
capability would be useful in C++ (as it is in Ada).

I concur.

--

-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- domain addr: rfg@netcom.com ----------- Purveyors of Compiler Test ----
---- uucp addr: ...!uunet!netcom!rfg ------- Suites and Bullet-Proof Shoes -




Author: barmar@think.com (Barry Margolin)
Date: 21 Feb 1994 00:49:59 GMT
Raw View
In article <kelley.761585674@debbie.fbsw.tt.com> kelley@debbie.fbsw.tt.com (Kevin Kelly) writes:
>Well, if you want to be able to derive from, and add to the functionality of,
>built-in types, there's always the method that Stroustrup mentioned:

[Class that defines a conversion to and from int]

>Now you can use Int's wherever an int would be accepted, and you can derive
>from Int and override whatever you want.

Not quite, because the language doesn't allow multiple automatic
conversions.  So a real int can be used where a float is expected, and it
will be coerced automatically, but an Int can't be.

However, this difference can probably be lived with.
--
Barry Margolin
System Manager, Thinking Machines Corp.

barmar@think.com          {uunet,harvard}!think!barmar




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Mon, 21 Feb 1994 11:47:57 GMT
Raw View
rodb@bridge.com (Rod Burman) writes:

> A colleague suggested deriving from a Rogue Wave int type class, so
> hat they had done all the drudge work, this would still (or might) not
> et me where I was going since unless the class, call it INT, which was
> esigned to work just like int, was an "aggregate" most compilers would
> ass it "as if by value" rather than by value, which might well be
> uite a performance hit, especially for something like an error class
> which would be passed/returned from nearly every function.

If you have a problem with the performance of simple int-like classes,
then this is a problem with the compiler(s), not the language.
There is nothing in the language definition that means than
passing small structs/classes by value need be any different to passing
builtin types by value.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: russgold@access.netaxs.com (Russell Gold)
Date: 21 Feb 1994 14:44:30 GMT
Raw View
Ronald F. Guilmette (rfg@netcom.com) wrote:
: In article <1830@racerx.bridge.COM> rodb@bridge.com (Rod Burman) writes:
: >
: >    I was trying to think of a simple method to get
: >a "type safe typdef", i.e. one that was not just a nick
: >name for the original type but also conferred type safety...

: Sort of a `typedef++', eh? :-)

: But seriously, someone else already noted (here, I believe) that such a
: capability would be useful in C++ (as it is in Ada).

: I concur.

I have to agree that such a feature would be nice, but I don't see how
it could ever be provided in a descendant of C. Ada is *very* strongly
typed, and does not do any implicit type conversions. For example:

 A : Integer;
 B : Short_Integer;

 A := B + 1;     -- illegal combination of types

is flagged by the compiler. You must say:
 A := Integer(B) + 1;   -- legal explicit type conversion.

This feature does allow for type-safety.  In C/C++, the equivalent
code:

 int a;
 short b;

 a = b+1;

is legal, via implicit type coertion. To get type safety in C++, you
would have to have a way of turning off the implicit type coertion,
and a way of turning it on only when you wanted to.  Note the
previously mentioned Stroustrup solution:

 class SpecialInt {
     public:
      SpecialInt( int );
      operator int() const;
     };

does not do this, as implicit coertion will still occur.
-----------------------------------------------------------------------------
Russell Gold                     | This account has nothing to do with my
russgold@netaxs.com (preferred)  | employer, and neither do any of my opinions.
russgold@aol.com                 |




Author: js@aron (Jerzy Sobczyk)
Date: 21 Feb 1994 15:51:01 GMT
Raw View
Russell Gold (russgold@access.netaxs.com) wrote:
: Ronald F. Guilmette (rfg@netcom.com) wrote:
: : In article <1830@racerx.bridge.COM> rodb@bridge.com (Rod Burman) writes:
: : >
: : >    I was trying to think of a simple method to get
: : >a "type safe typdef", i.e. one that was not just a nick
: : >name for the original type but also conferred type safety...
:
[.......]
:
: I have to agree that such a feature would be nice, but I don't see how
: it could ever be provided in a descendant of C. Ada is *very* strongly
: typed, and does not do any implicit type conversions. For example:
:
:  A : Integer;
:  B : Short_Integer;
:
:  A := B + 1;     -- illegal combination of types
:
: is flagged by the compiler. You must say:
:  A := Integer(B) + 1;   -- legal explicit type conversion.
:
: This feature does allow for type-safety.  In C/C++, the equivalent
: code:
:
:  int a;
:  short b;
:
:  a = b+1;
:
: is legal, via implicit type coertion. To get type safety in C++, you
: would have to have a way of turning off the implicit type coertion,
: and a way of turning it on only when you wanted to.  Note the
: previously mentioned Stroustrup solution:
:
:  class SpecialInt {
:      public:
:       SpecialInt( int );
:       operator int() const;
:      };

So remove the operator int() ;-) and replace it with member function:
 int SpecialInt::cast_int();
or friend finction:
 int cast_int( SpecialInt& x );
And add some other operators at your will.
 Jerzy
--
========================================================================
| Jerzy Sobczyk            Institute of Automatic  Control  /##/ /#####|
| J.Sobczyk@ia.pw.edu.pl   Warsaw University of Technology /##/ /##/|##|
| tlx. 813 307 pw pl              Nowowiejska 15/19       /##/ /##/ |##|
| fax. (+48)(22) 25 37 19           00-665 Warsaw,       /##/ /####>|##|
| tel. (+48)(22) 621 00 70 ext. 7297    POLAND          /##/ /##/   |##|
========================================================================
                     Temporarily in Trondheim, Norway




Author: rodb@bridge.com (Rod Burman)
Date: 16 Feb 94 15:56:58 GMT
Raw View
    I was trying to think of a simple method to get
a "type safe typdef", i.e. one that was not just a nick
name for the original type but also conferred type safety, but
without all the hassle of re-writing the class this would be
useful for classes with a lot of members for which you wish
to change a few (0,1..) behaviours but not the vast majority
and I cmae up with the following method (I've based the example
on roman numerals, but actually was thinking of an error reporting
class when I first had the idea, I'm sure there are other uses..)

#include <iostream.h>
class roman : public int {}

ostream &operator<<(ostream& os, roman r) {
{
    for (int i = r; i >= 1000; i -= 1000) {
        os << 'M';
    }
// further code elided for brevity
    return os;
}
 int main() {
     os << "Hello world (copyright " << roman(1970) << " K&R)" << endl;
}

Unfortunately none of the compilers I've tried this with like it!
I've read the relevant part of the reference manual (in the back
of the C++ language by Bjarne Stroustrop) and it doesn't help me,
but then I'm no language lawyer, it says you can derive from a class
which would seem to leave out int (and float, etc) but then aren't
int (etc) "builtin classes" in C++, is there any good reason to
stop the above construct and if a compiler allowed it would it work
as I hope? i.e. act as an int for arithmetic but have a special
output format.
      thanks for any info
   Rod Burman
usual disclaimers apply, haven't ATT, uSoft and IBM copyrighted all
possible word combinations by now?




Author: bbarring@batph104.NoSubdomain.NoDomain (Bill Barrington)
Date: Wed, 16 Feb 1994 17:37:24 GMT
Raw View
In article <1830@racerx.bridge.COM>, rodb@bridge.com (Rod Burman) writes:
|>
|>     I was trying to think of a simple method to get
|> a "type safe typdef", i.e. one that was not just a nick
|> name for the original type but also conferred type safety, but
|> without all the hassle of re-writing the class this would be
|> useful for classes with a lot of members for which you wish
|> to change a few (0,1..) behaviours but not the vast majority
|> and I cmae up with the following method (I've based the example
|> on roman numerals, but actually was thinking of an error reporting
|> class when I first had the idea, I'm sure there are other uses..)
|>
|> #include <iostream.h>
|> class roman : public int {}
|>
|> ostream &operator<<(ostream& os, roman r) {
|> {
|>     for (int i = r; i >= 1000; i -= 1000) {
|>         os << 'M';
|>     }
|> // further code elided for brevity
|>     return os;
|> }
|>  int main() {
|>      os << "Hello world (copyright " << roman(1970) << " K&R)" << endl;
|> }
|>
|> Unfortunately none of the compilers I've tried this with like it!
|> I've read the relevant part of the reference manual (in the back
|> of the C++ language by Bjarne Stroustrop) and it doesn't help me,
|> but then I'm no language lawyer, it says you can derive from a class
|> which would seem to leave out int (and float, etc) but then aren't
|> int (etc) "builtin classes" in C++, is there any good reason to
|> stop the above construct and if a compiler allowed it would it work
|> as I hope? i.e. act as an int for arithmetic but have a special
|> output format.
|>       thanks for any info
|>    Rod Burman
|> usual disclaimers apply, haven't ATT, uSoft and IBM copyrighted all
|> possible word combinations by now?

 Yeah, I think it would be really cool if we could redefine the
operations for all builtin types.  That probably wouldn't cause too many
problems, would it?

 Seriously, the class construct provides what you are looking for
(i.e., type safety), for user defined types.

 If you have a copy of the latest Journal of Object Oriented
Programming, look at the article by Andrew Koenig.  This is related to what
you are trying to achieve.  He discusses the rationale
for providing a new type for the C++ language (a boolean type).  A primary
motivator was the ability to provide behavior appropriate for a boolean type
that can only be achieved by making it a true type.  In other words, if boolean
was defined to be, say "typedef int boolean", then outputting this to a stream
can only result in doing what is appropriate for an int.  If, however, there
were a true boolean type, then the stream could do whatever is appropriate for
a boolean.

 Ints, floats, char*s, doubles, etc. are NOT "builtin classes" of the
C++ language.  There is NO way to "add to" or otherwise change their behavior.


Bill Barrington
bbarring@bnr.ca




Author: rodb@bridge.com (Rod Burman)
Date: 17 Feb 94 15:29:06 GMT
Raw View
Bill Barrington (bbarring@batph104.NoSubdomain.NoDomain) wrote:

: In article <1830@racerx.bridge.COM>, rodb@bridge.com (Rod Burman) writes:
: |> class roman : public int {}
-- much removed to save space

:  Yeah, I think it would be really cool if we could redefine the
: operations for all builtin types.  That probably wouldn't cause too many
: problems, would it?

:  Seriously, the class construct provides what you are looking for
: (i.e., type safety), for user defined types.

:  If you have a copy of the latest Journal of Object Oriented
: Programming, look at the article by Andrew Koenig.  This is related to what
: you are trying to achieve.  He discusses the rationale
: for providing a new type for the C++ language (a boolean type).  A primary
: motivator was the ability to provide behavior appropriate for a boolean type
: that can only be achieved by making it a true type.  In other words, if boolean
: was defined to be, say "typedef int boolean", then outputting this to a stream
: can only result in doing what is appropriate for an int.  If, however, there
: were a true boolean type, then the stream could do whatever is appropriate for
: a boolean.

:  Ints, floats, char*s, doubles, etc. are NOT "builtin classes" of the
: C++ language.  There is NO way to "add to" or otherwise change their behavior.


: Bill Barrington
: bbarring@bnr.ca
I think I must not have expressed myself clearly, I know I could do this with a
"large" class (i.e. one that overloaded most/all arithmetic) operators and I
know that overloading the operators on built-in types would be a Bad Idea(tm)
All I was trying to do was get a simple method for typesafing what is basically
an int, without having to code even trivial versions of +, -, *, ... (A colleague
suggested deriving from a Rogue Wave int type class, so that they had done all
the drudge work, this would still (or might) not get me where I was going since
unless the class, call it INT, which was designed to work just like int, was an
"aggregate" most compilers would pass it "as if by value" rather than by value,
which might well be quite a performance hit, especially for something like an
error class which would be passed/returned from nearly every function. How would
allowing my syntax or just having a Pascal like "type" command break existing code
or lead to wierd(tm) behaviour? To me it would seem a trivial enhancement to the
language, my would would not add new keywords, break existing programmes and I
suspect would be trivial to impliment. So can anyone tell me:

1) why it is a really bad idea (I have hundreds a week but I can't see that
 /why this one is)
2) How I could do it NOW, by some obvious trivial efficient method I have
 missed

Rod Burman
Thanks for listening (if you have been).




Author: grumpy@cbnewse.cb.att.com (Paul J Lucas)
Date: Thu, 17 Feb 1994 17:28:20 GMT
Raw View


Author: stt@spock.camb.inmet.com (Tucker Taft)
Date: Thu, 17 Feb 1994 18:02:55 GMT
Raw View
In article <1833@racerx.bridge.com>, Rod Burman <rodb@bridge.com> wrote:
>Bill Barrington (bbarring@batph104.NoSubdomain.NoDomain) wrote:
>
>: In article <1830@racerx.bridge.COM>, rodb@bridge.com (Rod Burman) writes:
> . . .
>:  Ints, floats, char*s, doubles, etc. are NOT "builtin classes" of the
>: C++ language.  There is NO way to "add to" or otherwise change their behavior.
>
>
>: Bill Barrington
>: bbarring@bnr.ca
>I think I must not have expressed myself clearly, I know I could do this with a
>"large" class (i.e. one that overloaded most/all arithmetic) operators and I
>know that overloading the operators on built-in types would be a Bad Idea(tm)
>All I was trying to do was get a simple method for typesafing what is basically
>an int,...

>... How would
>allowing my syntax or just having a Pascal
>like "type" command break existing code
>or lead to wierd(tm) behaviour? To me it
>would seem a trivial enhancement to the
>language, my would would not add new keywords, break existing programmes and I
>suspect would be trivial to impliment. So can anyone tell me:
>
>1) why it is a really bad idea (I have hundreds a week but I can't see that
> /why this one is)
>2) How I could do it NOW, by some obvious trivial efficient method I have
> missed

At this point I can no longer resist mentioning that
you might consider Ada.  It provides strong type distinctions
between user-defined numeric types, giving you the ability
to safely distinguish counts of Apples and Oranges,
Meters from Seconds, etc.  If used wisely, you can largely
eliminate at compile-time all possibility of out-of-bound
array indexing, and various other pernicious bugs, while
getting full optimization on the numeric types.

As far as adding strongly distinguished numeric types to C++,
it is probably not as simple as you suggest, because of array indexing
(a.k.a. adding ints to pointers ;-).  To get useful type checking,
you will want to be able to specify the index type of an array,
so the compiler will make sure that you use the appropriate
integer (or enum) type inside the "[]"s.  This would be possible if
you defined your own [] operator, but the default [] operator
(or "+" for ptr and int) could not make any distinction.

Furthermore, if you have multiple strongly distinguished integer
types, then you probably would want some way to specify the
"type" of an integer literal, or you would need to have some kind
of "universal integer" type for literals.



Author: g2devi@cdf.toronto.edu (Robert N. Deviasse)
Date: Thu, 17 Feb 1994 20:13:18 GMT
Raw View
>
In article <1833@racerx.bridge.com>, Rod Burman <rodb@bridge.com> wrote:
>Bill Barrington (bbarring@batph104.NoSubdomain.NoDomain) wrote:
>
>: In article <1830@racerx.bridge.COM>, rodb@bridge.com (Rod Burman) writes:
> . . .
>:  Ints, floats, char*s, doubles, etc. are NOT "builtin classes" of the
>: C++ language.  There is NO way to "add to" or otherwise change their behavior.
>
>
>: Bill Barrington
>: bbarring@bnr.ca
>I think I must not have expressed myself clearly, I know I could do this with a
>"large" class (i.e. one that overloaded most/all arithmetic) operators and I
>know that overloading the operators on built-in types would be a Bad Idea(tm)
>All I was trying to do was get a simple method for typesafing what is basically
>an int,...

>... How would
>allowing my syntax or just having a Pascal
>like "type" command break existing code
>or lead to wierd(tm) behaviour? To me it
>would seem a trivial enhancement to the
>language, my would would not add new keywords, break existing programmes and I
>suspect would be trivial to impliment. So can anyone tell me:
>
>1) why it is a really bad idea (I have hundreds a week but I can't see that
> /why this one is)
>2) How I could do it NOW, by some obvious trivial efficient method I have
> missed
>

Well this integer subranges, you can do this:

  template<class DUMMY,long lo,long hi>
    struct Subrange {
        enum Type { LOW=lo, HIGH=hi };  // All values in between are included
    };

  #define SUBRANGE_typedef(type,lo,hi)
    class dummy_type_##type;\
    typedef Subrange<dummy_type_##type*,lo,hi>::Type type;\
    inline type operator+(type t1,type t2) { return type(t1+t2); }\
    /* ... */                                                     \
    inline type operator*(type t1,type t2) { return type(t1*t2); }


So if you want to create two new overloadable integers, just use:
    SUBRANGE_typedef(Oranges,0,1)
    SUBRANGE_typedef(Apples,0,42)

    void f(Oranges);
    void f(Apples);
    void f(int);

    Oranges orange  = Oranges(0) ; // Okay
    Oranges orange2 = 0 ;          // Error, assignment of int to enum
    Apples  apple   = orange;      // Error, assignment of wrong enum to enum

    // These call the expected f()
    f(orange);
    f(0);


Admittedly this is an ugly hack, only applicable to integers, but it does
work.


--
/----------------------------------+------------------------------------------\
| Robert N. Deviasse               |"If we have to re-invent the wheel,       |
| EMAIL: g2devi@cdf.utoronto.ca    |  can we at least make it round this time"|
+----------------------------------+------------------------------------------/