Topic: Expanding enumerators in inheriting classes


Author: oherb@jhunix.hcf.jhu.edu (Zoher B Bambot)
Date: Tue, 25 Aug 1992 20:33:09 GMT
Raw View
I am working on developing classes for simulation of finite element meshes
using the object-oriented language c++. I would be grateful if anyone could
send me any programs in c++ which would help me in my work. Also has anyone
developed a class for the simulation of a FEM for any particular structure.
You can mail them to me at my e-mail address and then I will summarize in
the net. Also are there any ftp sites from where I could get any useful
information. Thanx in advance.
                         Zoher.




Author: pabloh@hpwala.wal.hp.com (Pablo Halpern )
Date: Wed, 26 Aug 1992 18:25:08 GMT
Raw View
In article <1992Aug17.062809.24177@lth.se>, dag@control.lth.se (Dag Bruck) writes:
|> In an earlier life I seriously doubted that enumerations could be
|> emulated with some other language elements.  That is of course a good
|> reason to try to do it anyway.
|>
|> Here is some code that uses templates to do something that is similar
|> in spirit to built-in enumerations.  I have deliberately thrown out
|> several safety measures in these classes to give the compiler a chance
|> to produce code with reasonable efficiency.
|>
|> I have managed to realize some important properties of built-in
|> enumerations:
|>
|>  1.  Typed symbolic values
|>  2.  Overloading of operators
|>  3.  Similar syntax of usage (not of declaration)
|>
|> As far as I can tell, there are a few fundamental differences between
|> the template enumeration and the built-in enumeration that we cannot
|> (or want to) get around:
|>
|>  1.  Template enumerators are not compile-time constants
|>  2.  Template enumerators cannot be used as case labels
|>  3.  There seems to be no good way to restrict the set
|>      of enumerators that belong to an enumeration

[ more discussion and code deleted ]

This discussion started out being about extending enumerations through
inheritance or an inheritance-like mechanism.  Dag's template-based
approach does this, but is limited in the ways he mentions.  I have another
approach that I have used in practice, and which doesn't require a compiler
that supports templates.  It has none of the flaws Dag mentions, although
it may have different flaws.  One flaw I know of is that it is not type
safe because any enumeration value can be assigned to any enumeration
variable, regardless of whether they are of the same type.  Because of
enumerator's equivalence to int, the same flaw exists in the built-in
enumeration feature of C++.

Basically, this scheme involves wrapping enum types in classes.  This has
the benefit of keeping the namespace of enumeration constants disjoint
between different enumeration types.  This system is an idiom; there is no
re-usable class library except for the base class, enumeration.  Therefore,
I must explain my system using an example:

  // re-usable base class
  class enumeration
  {
  public:
    // convert to/from int
    operator int() const { return _val; }
    enumeration& operator = (int i) { _val = i; return *this; }
    // Operators ==, !=, <, etc. can be implemented using int conversion
  protected:
    enumeration(int i = 0) { _val = i; }
    int _val;
  };

  // This class is created for a communications package
  class modemCommand : public enumeration
  {
  public:
    enum { DIAL, HANGUP, CHANGE_SPEED, _next };
    modemCommand(int i) : enumeration(i) {}
    modemCommand& operator = (int i) { _val = i; return *this; }
  };

  // This class is derived years later for a fax package
  class faxModemCommand : public modemCommand
  {
  public:
    // Note: first item starts where base class leaves off
    enum { SET_FAX_MODE = modemCommand::_next, SET_DATA_MODE, SET_VOICE_MODE,
           _next };
    faxModemCommand(int i) : modemCommand(i) {}
    faxModemCommand& operator = (int i) { _val = i; return *this; }
  };

  // Some uses of these classes
  enumeration e1;  // illegal: protected constructor
  modemCommand mc1 = modemCommand::DIAL;
  faxModemCommand fc1 = modemCommand::HANGUP;  // Okay
  faxModemCommand fc2 = faxModemCommand::HANGUP; // Same thing.
  modemCommand mc2 = faxModemCommand::SET_FAX_MODE;  // This is legal, but it
                                                     // shouldn't be.

Note the use of the _next value for derivation.  Each derived class defines an
enumeration that starts from the _next value of its imediate base class.  Of
course, each class can have additional member variables and functions
associated with it, if you want.  Some run-time type checking could be added,
if you want, by putting range checks in the copy constructors and assignment
operators.

--

- Pablo

------------------------------------------------------------------------
Pablo Halpern             (617) 290-3542
HP Waltham                pabloh@hpwarq.wal.hp.com

I am self-employed, so my opinions *do* reflect those of my employer.
However, they may not reflect the opinions of my client.
------------------------------------------------------------------------




Author: hildum@ntmtv.UUCP (Eric Hildum)
Date: Mon, 10 Aug 1992 20:08:14 GMT
Raw View
A language feature that I would like to see is support for inheritance upon
the builtin type enumerator. That is, I would like to define a number of
enumerators for an enumeration type, then use that enumeration type as a base
for a second enumerator with an expanded enumerator set, incorporating all the
base types enumerators and adding a number of new enumerators.

Has something like this extension been suggested anywhere?


    Eric Hildum




Author: Bruce.Hoult@bbs.actrix.gen.nz
Date: Tue, 11 Aug 1992 13:39:26 GMT
Raw View
In article <1992Aug10.200814.12809@ntmtv> hildum@ntmtv.UUCP (Eric Hildum) writes:
> A language feature that I would like to see is support for inheritance upon
> the builtin type enumerator. That is, I would like to define a number of
> enumerators for an enumeration type, then use that enumeration type as a base
> for a second enumerator with an expanded enumerator set, incorporating all the
> base types enumerators and adding a number of new enumerators.
>
> Has something like this extension been suggested anywhere?

When Wirth was adding object-oriented features to Modula-2 in creating
Oberon he used the term "type extension" where we C++ers would use
"derivation".

Being a fan of orthogonality (where sensible) he wanted to be able to apply
type extension to enumerated types.  It seems that he could find no good way
to do it and instead dropped enumerated types from the language entirely!

Perhaps there is something nice that Wirth missed (and I seem to
remember something about the problems in the Oberon documentation) but I
wouldn't bet the farm on it...  :-)

-- Bruce

(Dropping enumerations is one of the two things obviously missing from
Oberon that Wirth had in both Pascal and Modula-2.  The other is that
Oberon only allows pointers to records (structures), not to built-in
types or to arrays or other pointers.  I suspect the reason has
something to do with garbage collection.)
--
Bruce.Hoult@bbs.actrix.gen.nz   Twisted pair: +64 4 477 2116
BIX: brucehoult                 Last Resort:  PO Box 4145 Wellington, NZ
"Cray's producing a 500 MIPS personal computer with 256MB RAM and 8 GB
hard disk that fits in your pocket!"   "Great!  Is it PC compatible?"




Author: stt@spock.camb.inmet.com (Tucker Taft)
Date: Tue, 11 Aug 1992 19:08:16 GMT
Raw View
In article <1992Aug11.133926.28840@actrix.gen.nz> Bruce.Hoult@bbs.actrix.gen.nz writes:
>In article <1992Aug10.200814.12809@ntmtv> hildum@ntmtv.UUCP (Eric Hildum) writes:
>> A language feature that I would like to see is support for inheritance upon
>> the builtin type enumerator. That is, I would like to define a number of
>> enumerators for an enumeration type, then use that enumeration type as a base
>> for a second enumerator with an expanded enumerator set, incorporating all the
>> base types enumerators and adding a number of new enumerators.
>>
>> Has something like this extension been suggested anywhere?
>
>When Wirth was adding object-oriented features to Modula-2 in creating
>Oberon he used the term "type extension" where we C++ers would use
>"derivation".
>
>Being a fan of orthogonality (where sensible) he wanted to be able to apply
>type extension to enumerated types.  It seems that he could find no good way
>to do it and instead dropped enumerated types from the language entirely!
>
>Perhaps there is something nice that Wirth missed (and I seem to
>remember something about the problems in the Oberon documentation) but I
>wouldn't bet the farm on it...  :-)
>
>-- Bruce

We considered supporting enumeration type extension in Ada 9X, but
ultimately dropped it.  One issue (among others) was that values from distinct
enumeration extensions of a parent enumeration type
might have the same associated integer "code,"
making direct comparison between them less meaningful.  That is, equality
could be defined, but less-than/greater-than became relatively meaningfless.

One reason we lost interest was the observation that record
extension/derivation tends to make enumeration types less necessary.
Rather than having a variant record/union type with a value of an enumeration
type distinguishing the variants, one would simply define distinct
derivations.  A virtual function could return a string name
for each distinct derivation, corresponding to the enumeration literal.
A separate virtual function could return an integer code, if needed.

>Bruce.Hoult@bbs.actrix.gen.nz   Twisted pair: +64 4 477 2116
>BIX: brucehoult                 Last Resort:  PO Box 4145 Wellington, NZ
>"Cray's producing a 500 MIPS personal computer with 256MB RAM and 8 GB
>hard disk that fits in your pocket!"   "Great!  Is it PC compatible?"

S. Tucker Taft    stt@inmet.com
Intermetrics, Inc.
Cambridge, MA  02138




Author: hildum@ntmtv.UUCP (Eric Hildum)
Date: Wed, 12 Aug 1992 19:22:00 GMT
Raw View
In article <1992Aug11.190816.8623@inmet.camb.inmet.com>, stt@spock.camb.inmet.com (Tucker Taft) writes:
|>
|> We considered supporting enumeration type extension in Ada 9X, but
|> ultimately dropped it.  One issue (among others) was that values from distinct
|> enumeration extensions of a parent enumeration type
|> might have the same associated integer "code,"
|> making direct comparison between them less meaningful.  That is, equality
|> could be defined, but less-than/greater-than became relatively meaningfless.

Good point; however, C++ already allows duplicate codes, and it is not clear
why you would want (or would even be allowed to perform) comparisons between
DISTINCT enumeration extensions. I supose you could do it by defining a friend
function of both classes, in which case the meaning of such operations is up
to the implementor of the classes.


|>
|> One reason we lost interest was the observation that record
|> extension/derivation tends to make enumeration types less necessary.
|> Rather than having a variant record/union type with a value of an enumeration
|> type distinguishing the variants, one would simply define distinct
|> derivations.  A virtual function could return a string name
|> for each distinct derivation, corresponding to the enumeration literal.
|> A separate virtual function could return an integer code, if needed.
|>

That is run time type information via string compares, or via integer compares.
The problem with this is that now the programmer has to be concerned with the
exact values returned from each different class to prevent conflicts within
inheritance trees (and across such trees), whereas with enumeration extension
this could be done by the compilier automatically.

    Eric




Author: jbn@lulea.trab.se (Johan Bengtsson)
Date: 13 Aug 92 09:49:56 GMT
Raw View
hildum@ntmtv.UUCP (Eric Hildum) writes:
: In article <1992Aug11.190816.8623@inmet.camb.inmet.com>, stt@spock.camb.inmet.com (Tucker Taft) writes:
:
: |>
: |> One reason we lost interest was the observation that record
: |> extension/derivation tends to make enumeration types less necessary.
: |> Rather than having a variant record/union type with a value of an enumeration
: |> type distinguishing the variants, one would simply define distinct
: |> derivations.  A virtual function could return a string name
: |> for each distinct derivation, corresponding to the enumeration literal.
: |> A separate virtual function could return an integer code, if needed.
: |>
:
: That is run time type information via string compares, or via integer compares.
: The problem with this is that now the programmer has to be concerned with the
: exact values returned from each different class to prevent conflicts within
: inheritance trees (and across such trees), [...]

A better approach is to have each class return a reference to a unique
object, one for each class, like this:

virtual const Type& someClass :: myType();
{
 static Type theType; // unique type object
 return theType;
}

Since objects are guaranteed to have distinct addresses,
conflicts between class type identifiers are avoided.  Also,
the Type class can be extended with additional operations and
data.  You would probably want easy equality tests:

int operator == ( const Type& a, const Type& b )
{ return &a == &b; }
--
--------------------------------------------------------------------------
| Johan Bengtsson, Telia Research AB, Aurorum 6, S-951 75 Lulea, Sweden  |
| jbn@lulea.trab.se; Voice:(+46)92075471; Fax:(+46)92075490              |
--------------------------------------------------------------------------




Author: dag@control.lth.se (Dag Bruck)
Date: Fri, 14 Aug 1992 19:30:46 GMT
Raw View
In <comp.std.c++> hildum@ntmtv.UUCP (Eric Hildum) writes:
>
>... C++ already allows duplicate codes

Yes, but the definitions are at least in one place, which makes the
code easier to maintain.  Never mind, I'm sure you are aware of the
potential problems.

I have implemented extensible enumerations with templates.  I think
the template sollution offers much of what you would like:

 - extensible set of enumerators
 - named enumerator symbols
 - type-safe, in the sense that an enumeration variable
   may only be assigned enumerators of the corresponding
   (possibly extended) enumeration

However, there are problems:

 - the template version is MUCH less efficient, at least
   with current template implementations
 - these enumerators are not compile-time constants, so
   they cannot be used as case labels, for example
 - compiler must allocate storage for enumerators
 - the scope of the enumerator may not match the scope
   of the enumerated type, so garbage values may float
   around in the system

It is possible to add additional logic to handle the last problem
listed above, but it does cost a little in run-time performance.

I did this excercise as part of the analysis of operator overloading
of enumerations (a C++ standardization issue).  I can post the actual
code if there is enough interest; it's just two pages of code.


  -- Dag




Author: dag@control.lth.se (Dag Bruck)
Date: Mon, 17 Aug 1992 06:28:09 GMT
Raw View
In an earlier life I seriously doubted that enumerations could be
emulated with some other language elements.  That is of course a good
reason to try to do it anyway.

Here is some code that uses templates to do something that is similar
in spirit to built-in enumerations.  I have deliberately thrown out
several safety measures in these classes to give the compiler a chance
to produce code with reasonable efficiency.

I have managed to realize some important properties of built-in
enumerations:

 1.  Typed symbolic values
 2.  Overloading of operators
 3.  Similar syntax of usage (not of declaration)

As far as I can tell, there are a few fundamental differences between
the template enumeration and the built-in enumeration that we cannot
(or want to) get around:

 1.  Template enumerators are not compile-time constants
 2.  Template enumerators cannot be used as case labels
 3.  There seems to be no good way to restrict the set
     of enumerators that belong to an enumeration

The last issue is probably worth a few words.  Template enumerators
can be created and destroyed dynamically, for example as variables
local to a function. In this version there is no way to prevent the
bit-pattern of a destroyed enumerator from floating around the
program; with built-in enumerators you at least have to use an
explicit type cast to create illegal enumerators.

The other side of this problem is that the person who defines a
template enumeration cannot assume a fixed value domain; the
application may add new enumerators to the original set.

There are also some issues regarding efficiency in a broad sense:

 1.  Template enumerators are more awkward to declare and
     the compiler must allocate storage for them
 2.  The implementation is much less efficient, at least
     on my system
 3.  The user must learn a class library in addition to the
     built-in feature

Finally, my compiler will not allow template class specialization if
the member function is defined as inline.  I assume that this is an
implementation restriction, and not in the language.

An alternative approach is to toss out class Enumerator and create
class Enumeration parameterized with a built-in enumeration.  This
approach allows operator overloading of variables of class
Enumeration, but neither overloading of the enumerators nor a dynamic
set of enumerators.

   -- Dag

Code follows:

// An attempt to emulate enums with template classes.  This is the really
// lean version without any checking at all.
//
// Author: Dag Bruck, Department of Automatic Control, Lund Institute of
// Technology, Box 118, S-221 00 Lund, Sweden.  E-mail: dag@control.lth.se

// Declaration of class Enumerator

template <class E>
class Enumerator {
public:
  Enumerator() : val(++highest_value) {}
  Enumerator(int x) : val(x) { highest_value = val; }
  operator int () const { return val; }

private:
  int val;
  static int highest_value;
};

// Declaration of class Enumeration

template <class E>
class Enumeration {
public:
  Enumeration() : val(0) {}
  Enumeration(const Enumerator<E>& x) : val(x) {}
  Enumeration<E>& operator = (const Enumeration<E>& e)
    { val=e.val; return *this; }
  Enumeration<E>& operator |= (const Enumeration<E> e);
//  { val |= e.val; return *this; }  /* Because of compiler restriction? */
  operator int () const { return val; }

private:
  int val;
};

template <class E>
int Enumerator<E>::highest_value = -1;

template <class E>
inline Enumeration<E> operator | (const Enumeration<E> e1,
                                  const Enumeration<E> e2)
{
  Enumeration<E> r(e1);
  r |= e2;
  return r;
}

// Test of class Enumeration
//
// - we overload the meaning operator |= to get the desired behaviour
//   for OFAscore

struct OFAscore {};

Enumerator<OFAscore> severe, bad, marginal, fair, good, excellent;
   /* there is another one, but I forgot the name */

inline Enumeration<OFAscore>&
Enumeration<OFAscore>::operator |= (const Enumeration<OFAscore> e)
{
  if (e.val < val) val = e.val;
  return *this;
}

void main()
{
  Enumeration<OFAscore> left_hip, right_hip, total_score;
  left_hip = fair;
  right_hip = good;
  total_score = left_hip | right_hip;
}