enum-1.5.2 module for Tcl - Enumerated Types.

Released: 2006-08-14

Maintainer: julian@webpub.net
License: Same as Tcl (BSD-style)

1.5.2 is a bugfix release.
(enum::bin2i didn't properly account for unsigned values - i.e it was sometimes wrong)

Download enum-1.5.2.tm

> package require enum ?1.5.2?

> enum create typename namelist
creates a new enumerated type typename mapping the string values in namelist to a corresponding list of integers starting at zero.
> enum create DAY {mon tue wed thu fri sat sun}
(DAY is capitalized here merely for clarity - this is not a requirement.)
> enum create FRUIT {peach pear cherry mangosteen}

> enum types
return the list of currently defined enumerated types.
> enum types

> enum typename key
return the name or integer corresponding to key
i.e if key is an integer - return the corresponding name
if key is a name - return the corresponding integer
> enum DAY wed
(note that the list is zero-based - i.e jan is 0 not 1!)
> enum DAY 5
> enum DAY spud
(raises an error: "unknown DAY 'spud'")
> enum DAY 20
(raises an error: "no DAY for '20'")

The above form 'enum typename key' is simple, yet does not always make for the clearest code.
> enum DAY $day
It is not immediately apparent whether the above returns a name or a number.
For clarity, and to help catch mistakes; consider using the following forms:
> enum number typename key
> set day "wed"
> enum number DAY $day
> set day 4
> enum number DAY $day
(raises an error: "DAY has no number for '4' - try 'enum nam DAY 4' or another key")
> enum name typename key
> enum name DAY 4
> enum name DAY sat
(raisesan error: "DAY has no name for 'sat' - try 'enum num DAY sat' or another key")
The shortcuts 'enum nam ...' and 'enum num ..' can also be used

> enum values typename
return the list of names for the type.
> enum values DAY
mon tue wed thu fri sat sun

> enum diff typename key1 key2
return the positive or negative difference between two keys.
(think of it as subtracting key2 from key1)
> enum diff DAY fri mon
> enum diff DAY mon fri
> enum diff DAY fri 1

> enum dist typename key1 key2
similar to diff, but returns only the distance between keys i.e it is unsigned
> enum dist DAY fri mon
> enum dist DAY mon fri

> enum next typename key
Acts as if we had called 'enum typename' with the next key above the one supplied.
> enum next DAY wed
> enum next DAY 5

Basic equality tests between two keys can be done with the operators: ==, !=, <, <=, >, >=
> enum typename key1 operator key2
This returns 1 or 0
> enum DAY fri > mon
This format is basically equivalent to something like:
> expr {[enum DAY fri] > [enum DAY mon]}

> enum vnext typename key
Also acts as if we had called 'enum typename' with the next key above the one supplied.
This time the result is the same type as the key e.g
> enum vnext DAY wed
> enum vnext DAY 5
> enum vnext DAY sun
(raises an error: "no next DAY for '6'")

Similarly we have:
> enum prev typename key
> enum vprev typename key

When thinking about 'next' vs 'vnext' and 'prev' vs 'vprev'
remember that the most natural purpose of enum to return the opposite type of key to that supplied.
- e.g 'enum months jan' returns 0 while 'enum months 0' returns jan
Therefore it is the 'unusually' named functions vprev & vnext that violate this and return the same type of key as that which was given.
Note that it is usually not particularly useful to use vnext & vprev with a numeric key, as it could simply be done with expr, except that using these functions will raise an error when the *result* of a call would be outside of an enum's range.

Instead of an enumeration consisting simply of incrementing integers, enum can be used with values that are powers of 2 - suitable for bitmasks.
> enum create typename namelist -bitmask 1
> enum create DAY {mon tue wed thu fri sat sun} -bitmask 1
> enum DAY fri
> enum DAY sat
> enum DAY sun
You can then use bitwise binary operations to create flags and set & test bits
> set training [enum DAY mon | wed | sat]
Now days can be tested against the training flag using either their name or value
> enum DAY $training & mon
> enum DAY $training & tue
> enum DAY $training & 64
> enum DAY $training & sat
A single element can be unset from the flag using the &~ operator:
> set training [enum DAY $training &~ sat]
> enum DAY $training & sat

There are some conversion functions available for convenience.
> enum bits DAY mon | tue | fri | 64
> enum hex DAY mon | tue | fri | 64
> set binstring [enum bin DAY mon | tue | fri | 64]
> string length $binstring
> enum::bin2i $binstring

Note that if you create a bitmasked enumeration of a large number of items, the powers of 2 get somewhat unwieldy.

 for {set i 0} {$i < 100000} {incr i} {
    lappend list k$i
 enum create BIG $list -bitmask 1
> enum BIG k222
> string length [enum BIG k222]
Now that's at the smaller end of the enumeration!
> string length [enum BIG k55555]
With numbers as large as these, converting the result to a string just to test it's length is an expensive operation
Tcl has to convert the number from it's internal numerical representation to a string representation.
The commandline above for k55555 can take a few seconds to return depending on your hardware.
e.g - on an old 400Mhz machine
> time {string length [enum BIG k55556]}
5641000 microseconds per iteration
This is faster if called a second time because now the string rep for k55556 exists
> time {string length [enum BIG k55556]}
875000 microseconds per iteration
Note, that if you're careful not to force a string rep - the operations are actually still quite usable
even at the upper end of such oversized enumerations.
e.g (1st time round) > time {set val [enum BIG k99998]}
15000 microseconds
> time {set val [enum BIG k99998]} 1000
266.0 microseconds per iteration

You can use the 'enum bin typename key ?op? ?key? ... ' syntax to store a set of flags in a compact binary form
but unless you have a particular need to use bitmask operations, you should probably just use Tcl arrays if you have a large numbers of flags.

> enum destroy typename
remove the enumerated type named typename

This package uses the Tcl >= 8.5 module system
(See http://mini.net/tcl/tcl modules)

This module was wrapped using the tarpack package and is also a valid tar archive.
Simply place it in one of the folders on your "Module path". No need to unarchive. Because it is a simple Tcl-only package, it is stand-alone and you do not require the 'tarpack' package.

To determine your module path folders; type the command "::tcl::tm::list" in a Tcl interp.
For example, on a windows platform, a folder such as c:\tcl\lib\site-tcl might be a suitable choice.

If you do choose to unarchive a module on your module path - the 'package require' operation will in effect be deflected by the .tm file to the unarchived version.
i.e the unpacked code will take precedence.

(This 'deflection' to the unwrapped version does require the 'tarpack' package)

BACK to packages