is a badly designed language and its designers should feel bad.
I say this for several reasons which I will soon impart to you.
1. How many types are too many?
Basically, Ada allows you to define all sorts of types. You can define a type which is all positive integers. You can define a type which is all real numbers between 2 and 3. You can define a type which only represents a valid number of seconds to occur in a day. At first, this sounds like a great idea because it forces you to make sure that all of your numeric values are legitimate and verifies them at runtime. The downside is that you have to cast every single time you move from one to another
. It doesn't matter if two different types are both defined to be all positive numbers representable with an unsigned 32 bit integer, you still have to explicitly cast. This becomes even worse if you're doing such simple things as adding, where if you're adding two different kinds of numeric types, you'll have to cast one into the other. Also, if you're using the built-in functions for doing things like converting an integer into a string, you have to know the exact type that you're using or else you won't know what function to call, and loathe to you who would try to use the wrong one.
2. What's that about interfaces?
So, sometimes you have to deal with binary interfaces. In my case, I'm reading satellite data which is in a fixed (and not going to change) format. Ada does, luckily, provide something similar to C structs called records which you can theoretically use to read such binary data in fixed structs. Unfortunately, the language (or at least the gnat implementation) made an idiotic design decision: if you're packing a record, the compiler can feel free to rearrange the elements of the record
(see 8 in link). Now, that just isn't going to work when the layout of the data has been pre-defined. When I say that the record is an int32, a uint8, and then an int32, it isn't ok to put the uint8 at the end to keep alignment easy!
3. Shift what now?
So, you can't use a record to just read the data in straight. What do you do instead? You use an array of bytes (Unsigned_8s in Ada parlance) which is the same size as the record that you're trying to read and then reconstruct them into the various types that you need. Sounds like no problem to me, just a bunch of shifting and ORing then some type fiddling to make everybody happy. Unfortunately, however, Ada only provides bit shifting operators for "modular" (i.e., unsigned) types. Also, due to the crazy type system mentioned in part one, you can't force a negative integer value which you've constructed in say a Unsigned_32 into an Integer_32 by using the casting operations. Clearly, that wouldn't make sense because the "negative" value in the Uint32 is larger than the maximum value of the Int32! How do you get around this impasse? By doing two's complement
calculations on your own, of course! This, along with my complaints in part 1, make the code to convert 4 bytes into a 32 bit integer something like 10 lines long instead of being more like the one line long piece of C code that I use to process other data plus it has lots of wonderful constants like 16#80000000# (which is the bitmask for seeing if the MSB is set) and 16#7FFFFFFF# (the negation of that for anding out the other part).
4. What do you mean it doesn't matter?
Ada is a case insensitive language. I don't think this is inherently a bad design decision, but it has, in the code base that I'm working with, led to the brilliant decision to use ALL UPPERCASE to refer to all non-built-in types, functions, and variables.
5. I would kill for fprintf.
Ada's options for writing numbers to text files are rather sparse. You can use Type'Image(variable) to produce a string which contains the value in a fixed format (integers would match the regex /(-| )\d+/; floats would match the regex /(-| )\d+\.\d+E(\+|-)\d+/). This has the downside of producing very ugly floating point numbers every positive number is have a space at the front of it
. It's pretty easy to remove, but I shouldn't have to. The other method is to create an instance of the Ada.Text_IO.Integer_IO or Ada.Text_IO.Float_IO classes. Of course, these classes can't be instantiated directly, you have to extend them so that they apply to a specific type of integer or float and won't work on any other types (see part 1 again). That's one extended class for each type or a whole lot of casting. Good luck trying to use integer/float IO at the same time as doing regular "text" IO. I wasn't able to ever get it working right. I'm sure there is a way; I'm also sure that it is bad and that it should feel bad.
Published by XPost