weberc2
weberc2

Reputation: 7958

Convert Enum to Binary (via Integer or something similar)

I have an Ada enum with 2 values type Polarity is (Normal, Reversed), and I would like to convert them to 0, 1 (or True, False--as Boolean seems to implicitly play nice as binary) respectively, so I can store their values as specific bits in a byte. How can I accomplish this?

Upvotes: 4

Views: 1160

Answers (5)

weberc2
weberc2

Reputation: 7958

It seems all I needed to do was pragma Pack([type name]); (in which 'type name' is the type composed of Polarity) to compress the value down to a single bit.

Upvotes: 0

T.E.D.
T.E.D.

Reputation: 44824

Two points here:

1) Enumerations are already stored as binary. Everything is. In particular, your enumeration, as defined above, will be stored as a 0 for Normal and a 1 for Reversed, unless you go out of your way to tell the compiler to use other values.

If you want to get that value out of the enumeration as an Integer rather than an enumeration value, you have two options. The 'pos() attribute will return a 0-based number for that enumeration's position in the enumeration, and Unchecked_Conversion will return the actual value the computer stores for it. (There is no difference in the value, unless an enumeration representation clause was used).

2) Enumerations are nice, but don't reinvent Boolean. If your enumeration can only ever have two values, you don't gain anything useful by making a custom enumeration, and you lose a lot of useful properties that Boolean has. Booleans can be directly selected off of in loops and if checks. Booleans have and, or, xor, etc. defined for them. Booleans can be put into packed arrays, and then those same operators are defined bitwise across the whole array.

A particular pet peeve of mine is when people end up defining themselves a custom boolean with the logic reversed (so its true condition is 0). If you do this, the ghost of Ada Lovelace will come back from the grave and force you to listen to an exhaustive explanation of how to calculate Bernoulli sequences with a Difference Engine. Don't let this happen to you!

So if it would never make sense to have a third enumeration value, you just name objects something appropriate describing the True condition (eg: Reversed_Polarity : Boolean;), and go on your merry way.

Upvotes: 1

trashgod
trashgod

Reputation: 205885

3.5.5 Operations of Discrete Types include the function S'Pos(Arg : S'Base), which "returns the position number of the value of Arg, as a value of type universal integer." Hence,

Polarity'Pos(Normal) = 0
Polarity'Pos(Reversed) = 1

You can change the numbering using 13.4 Enumeration Representation Clauses.

...and, of course:

Boolean'Val(Polarity'Pos(Normal)) = False
Boolean'Val(Polarity'Pos(Reversed)) = True

Upvotes: 3

user1818839
user1818839

Reputation:

An easy way is a lookup table:

Bool_Polarity : constant Array(Polarity) of Boolean 
              := (Normal=>False, Reversed => True);

then use it as

 B Boolean := Bool_Polarity(P);

Of course there is nothing wrong with using the 'Pos attribute, but the LUT makes the mapping readable and very obvious.

As it is constant, you'd like to hope it optimises away during the constant folding stage, and it seems to: I have used similar tricks compiling for AVR with very acceptable executable sizes (down to 0.6k to independently drive 2 stepper motors)

Upvotes: 3

user571138
user571138

Reputation:

I think what you are looking for is a record type with a representation clause:

procedure Main is

   type Byte_T is mod 2**8-1;
   for Byte_T'Size use 8;

   type Filler7_T is mod 2**7-1;
   for Filler7_T'Size use 7;

   type Polarity_T is (Normal,Reversed);
   for Polarity_T use (Normal => 0, Reversed => 1);
   for Polarity_T'Size use 1;

   type Byte_As_Record_T is record
      Filler   : Filler7_T;
      Polarity : Polarity_T;
   end record;

   for Byte_As_Record_T use record
      Filler at 0 range 0 .. 6;
      Polarity at 0 range 7 .. 7;
   end record;
   for Byte_As_Record_T'Size use 8;

   function Convert is new Ada.Unchecked_Conversion
     (Source => Byte_As_Record_T,
      Target => Byte_T);

   function Convert is new Ada.Unchecked_Conversion
     (Source => Byte_T,
      Target => Byte_As_Record_T);

begin

   -- TBC
   null;

end Main;

As Byte_As_Record_T & Byte_T are the same size, you can use unchecked conversion to convert between the types safely.

The representation clause for Byte_As_Record_T allows you to specify which bits/bytes to place your polarity_t in. (i chose the 8th bit)

My definition of Byte_T might not be what you want, but as long as it is 8 bits long the principle should still be workable. From Byte_T you can also safely upcast to Integer or Natural or Positive. You can also use the same technique to go directly to/from a 32 bit Integer to/from a 32 bit record type.

Upvotes: 2

Related Questions