With GNAT, I'm trying to print out System.Min_Int
Ada.Integer_Text_IO.Put(System.Min_Int);
Yields this:
"warning: value not in range of type "Ada.Text_Io.Integer_Io.Num" "
I also tried
Ada.Integer_Text_IO.Put_Line(Integer'Image(System.Min_Int));
Which yields:
value not in range of type "Standard.Integer"
How can I print System.Min_Int ?
System.Min_Int and System.Max_Int are named numbers. Logically they are of type universal_integer. They can be implicitly converted to an integer type (just like integer constants like 42), but of course the type needs to be big enough to hold it.
There is no predefined integer type that's guaranteed to be able to hold the values of System.Min_Int and System.Max_Int. An implementation isn't even required to define Long_Integer, and Integer is only required to be at least 16 bits.
Fortunately it's easy to define your own integer type with the necessary range.
with Ada.Text_IO;
with System;
procedure Min_Max is
type Max_Integer is range System.Min_Int .. System.Max_Int;
begin
Ada.Text_IO.Put_Line("System.Min_Int = " & Max_Integer'Image(System.Min_Int));
Ada.Text_IO.Put_Line("System.Max_Int = " & Max_Integer'Image(System.Max_Int));
end Min_Max;
The output on my system:
System.Min_Int = -9223372036854775808
System.Max_Int = 9223372036854775807
Confusingly, System.Min_Int in at least one recent Gnat, appears to be a Long_Integer (though as Simon points out, it is actually Long_Long_Integer, and on some compilers but not all, these have the same range).
So, the following works (in gcc4.9.3):
Put_Line(Long_Integer'Image(System.Min_Int));
reporting -9223372036854775808.
And so does Ada.Long_Integer_Text_IO.Put(System.Min_Int);
On the other hand, you may have been trying to find the minimum value of the Integer type, which is ...Integer'First, and sure enough,
Put_Line(Integer'Image(Integer'First));
reports -2147483648
The rationale for the difference is that Ada can support an uncountable number of integer types, but provides a few default ones like Integer for convenience.
System.Min_Int and friends reflect the limits of your specific system : attempting to declare larger integer types is legal, but will not compile on your system (i.e. until you upgrade the compiler).
In normal use, you will either use Integer or better, integer typ4es you declare with ranges appropriate to your problem. And the limits of each such type obviously can't be built into the language or even the System package. Instead, you use the predefined attributes, such as 'First and 'Last to query the relevant integer type.
So you can explore your machine's limits with the following:
with Ada.Text_IO; use Ada.Text_IO;
with System;
procedure pmin is
type Big_Integer is range System.Min_Int ..System.Max_Int;
package Big_Integer_IO is new Integer_IO(Num => Big_Integer);
begin
Big_Integer_IO.Put(System.Min_Int);
Put(" to ");
Big_Integer_IO.Put(System.Max_Int);
New_Line;
end pmin;
Here (gcc4.9.3) I get the result:
-9223372036854775808 to 9223372036854775807
If System.Min_Int is no longer in the range of Long_Integer in Gnat/gcc 6.1 I'm curious to see what this does on your system. Please add your test result in a comment.
Related
You can assign to a variable by having a function return a value to it:
My_Int : Integer := My_Math_Func [(optional params)];
Or you can do it like this with a procedure (assuming My_Int has already been declared):
My_Math_Proc ([optional params;] [in] out My_Int);
Obviously a procedure can't initialize a variable like the function does in the first example, but I'm hoping for some concrete, practical rules on when and why to pick one over the other.
Two to get you started...
When more than one result is to be returned, a procedure with several OUT parameters is often a good choice.
When the size of the object is unknown prior to the subprogram call, an OUT parameter cannot be used because it would have to be declared precisely the right size, but a function return can set the size by initialising the variable in the caller. This is commonly used with a variable declared in a Declare block, which can hold a different sized string each time it is invoked.
This example shows the variable "text" initialised by calling a Read_File function, to hold the contents of a different file on each iteration of the loop. Safe, no "malloc" or "free" or pointers necessary. (Filename is an array of filenames in this example)
for i in 1 .. last_file loop
declare
text : String := Read_File(Filename(i));
-- the size of "text" is determined by the file contents
begin
-- process the text here.
for j in text'range loop
if text(j) = '*' then
...
end loop;
end
end loop;
Edit : And I suppose I'd better mention the underlying mathematical principle, since Ada is based more closely on mathematical logic than many other languages.
Functions and procedures are both subprograms, but for different purposes:
a function is an abstraction over an expression : like a mathematical operator (and an operator in Ada is just a function). Ideally, it provides a result from a number of operands and nothing else, leaving them unchanged and having no state and no side effects. This ideal is called a "pure function" (and applying "pragma pure" asks the compiler to check its purity) - similar restrictions apply in functional programming (FP) languages. Pure functions allow a whole bunch of optimisation (because reordering them doesn't change results). In practice Ada is not that strict, allowing impure functions too.
a procedure is an abstraction over a statement. It generally has some physical effect (such as changing state) since it doesn't deliver a result.
So the logical separation between expressions and statements is carried over into subprograms (abstractions) as the separation between functions and procedures.
And this is probably the best way to decide which to use.
Brian Drummond already answered your question directly, but I wanted to add some additional info: If your type has some sort of initializing procedure, in Ada2005/Ada2012 you can convert it to an initializing function using extended return syntax. It will even work for limited types.
Say you have a package with a type like this:
package Example is
type My_Type is limited private;
procedure Initialize(Self : in out My_Type; Value : Integer);
procedure Print(Self : My_Type);
private
type My_Type is limited record
Value : Integer := 0;
end record;
end Example;
package body Example is
procedure Initialize(Self : in out My_Type; Value : Integer) is
begin
Self.Value := Value;
end Initialize;
procedure Print(Self : My_Type) is
begin
Ada.Text_IO.Put_Line(Self.Value'Image);
end Print;
end Example;
You can then make your own initializing function out of that procedure doing something like this:
function Make_My_Type (Value : Integer) return Example.My_Type is
begin
return Result : Example.My_Type do
Example.Initialize(Result,Value);
end return;
end Make_My_Type;
and you can initialize variables easily using the procedure hidden in your function underneath:
procedure Test
Thing : Example.My_Type := Make_My_Type(21);
begin
Example.Print(Thing);
end Test;
This is different than just making a variable and returning it. You are not able to do that with a limited type, but with extended return syntax, you can do it for any type.
Here is some additional info for extended return statements as well.
I'm a very beginner of Ada code. I use GPS from AdaCore.
I would create a variable sized by the user.
I write this :
-- My ada program --
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure main is
wanted : Integer := 10;
type custom is range 0..wanted;
...
But something went wrong in line 8 :
Builder results
C:\Users\**********\Desktop\ada project\src\main.adb
8:26 "wanted" is not static constant or named number (RM 4.9(5))
8:26 non-static expression used for integer type bound
I really don't understand what this mean... Can someone help me ?
Variable wanted is not a constant, it may change its value during program execution, therefore this variable is not allowed to be used as range constraint when declaring new types. You may however make it constant by using constant keyword (Wanted : constant Integer := 10;). It should resolve your problem.
As said by Timur, wanted has to be constant in its scope. This allows you some nice things such as declaring a type inside a procedure. Look at this, it might be of interest :)
-- My ada program --
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Main is
procedure Test (Wanted : Integer) is
type custom is new Integer range 0..wanted;
begin
Put_Line("First value " & Custom'Image (Custom'First)
& " Last value " & Custom'Image (Custom'Last));
end Test;
begin
Test (10);
Test (12);
end Main;
Output is
First value 0 Last value 10
First value 0 Last value 12
In this case, your type is different from one call to another but it works as wanted is constant within the procedure. The only thing is that the type defined has to be a new derived type of the type of your parameter.
I let you think about the possibilities :)
In Java or C#, you would often have class members that are final or readonly - they are set once and then never touched again. They can hold different values for different instances of the class.
Is there something similar in Ada? I've tried to create something similar in Ada thusly:
package MyPackage is
type MyObject is limited new OtherPackage.Object with private;
....
private
type MyObject (...) is limited new OtherPackage.Object with
record
M_MyField : Integer := 10;
M_MyConstantFactory : constant Factory.Object'Class := new Factory.Object;
end record;
end MyPackage;
This fails on the declaration of M_MyConstantFactory saying constant components are not permitted. Is there a way around this? A colleague suggested declaring it somewhere else in the package, but that would mean a single M_MyConstantFactory shared across all instances, which is not what I want.
Do I need to just accept that it is possible to modify the value once set and manually guard against that happening?
No. Not quite.
If your component is of a discrete type or an access type, you can make it a discriminant, and thus make it immutable.
with Ada.Integer_Text_IO;
procedure Immutable_Components is
type Instance (Immutable : Positive) is null record;
A : Instance := (Immutable => 1);
begin
Ada.Integer_Text_IO.Put (A.Immutable);
-- A.Immutable := 2; -- assignment to discriminant not allowed:
end Immutable_Components;
Before I answer the question, it would probably be helpful to distinguish between Ada and Java/C#'s modeling of objects. In Java, everything is an object, and so all constants have to be final -- in Ada things are a bit different, Ada's object-system ("tagged types" in Ada parlance) is built upon two items: records and type-derivation. This means that, when teaching OOP, we could arrive gradually introducing first type-derivation (eg Type Degree is new Integer;), then records (ie encapsulation), then private-types (ie information-hiding), and finally unifying everything together with tagged-types... all of which I'll assume you are cognizant of.
In Ada, a constant is just that: some object that can be read but [generally] not written to. (Things can get funny with eg memory-mapped IO.) So we could say:
Package Ex1 is
Type Stub1 is private; -- Some type, with almost nothing public.
C1 : Constant Stub1; -- A constant of that type.
Private
Type Stub1 is tagged record
Data_1 : Integer;
Data_2 : Float;
end;
-- And now we can tell the compiler what C1 _is_.
C1: Constant Stub1 := (Data_1 => 3, Data_2 => 1.2);
End Ex1;
That's how we'd make a constant for a tagged type while keeping its implementation details hidden; though, admittedly, we could have exposed everything and gotten rid of the entire private section.
Now we get to an interesting feature of records [and tagged types] called discriminants -- these are kind of like constants, and kind of like generic-types in other languages. With discriminants we could make a message-type that varies in size according to the message-length:
Package Ex2 is
Type Message(Length : Natural) is private; -- A message.
Function Create( Text : String ) return Message;
Private
Type Message(Length : Natural) is record
Data : String(1..Length) := (Others => ' '); -- Defaults to space-filled string.
end;
Function Create( Text : String ) return Message is
( Data => Text, Length => Text'Length );
End Ex2;
Now, in this case, when you do an assignment like X : Message := Create("Steve"); the variavle's type [unconstrained, in this example, becomes constrained in this case to Message(5) (because "Steve" is 5 characters) and so trying to re-assign with a different-sized message-string wouldn't work. (So, while you couldn't say X:= Create("Why") you can say X:= Create("Hello") because the discriminant [Length] here is 5.) -- So, in that manner discriminants can act like constant fields in some cases.
The limited keyword means that the type doesn't have an assignment [but does have initialization], so you could make the entire type behave as a constant; this is different than having the one component be constant though, certainly not as subtle as the distinction between T and T'Class (T'Class is the Type T and all types derived therefrom, where as T is only that type.)
You almost have the general solution (for cases when it can't be a discriminant) already. The type is limited private. Clients of the package can only modify it through the operations the pkg provides. As long as the operations don't modify the field in question, you have what you want (unless I misunderstood and the question is how to prevent yourself from modifying the field in the pkg body).
I want to declare a constant as a 16 bit integer of type Word and assign a value to it. To support portability between Big and Little Endian platforms, I can't safely use an assignment like this one:
Special_Value : Constant Word := 16#1234#;
because the byte order might be misinterpreted.
So I use a record like this:
Type Double_Byte Is Record
Byte_1 : Byte; -- most significant byte
Byte_0 : Byte; -- least significant byte
End Record;
For Double_Byte Use Record
Byte_1 At 0 Range 0..7;
Byte_0 At 0 Range 8..15;
End Record;
However, in some cases, I have a large number of pre-configuration assignments that look like this:
Value_1 : Constant Word := 15#1234#;
This is very readable by a person, but endian issues cause it to be misunderstood a number of ways (including in the debugger, for example).
Because I have many lines where I do this, I tried the following because it is fairly compact as source code. It is working, but I'm not sure why, or what part of the Ada Reference Manual covers this concept:
Value_1 : Constant Word := DByte_To_Word((Byte_1 => 16#12#,
Byte_0 => 16#34#));
where DByte_To_Word is defined as
Function DByte_To_Word Is New Unchecked_Conversion(Double_Byte, Word);
I think I have seen something in the ARM that allows me to do this, but not the way I described above. I can't find it and I don't know what I would be searching for.
There’s nothing unusual about your call to DByte_To_Word; (Byte_1 => 16#12#, Byte_0 => 16#34#) is a perfectly legitimate record aggregate of type Double_Byte, see LRM83 4.3.1.
But! But! it’s true that, on a big-endian machine, the first (lowest-addressed) byte of your Word will contain 16#12#, whereas on a little-endian machine it will contain 16#34#. The CPU takes care of all of that; if you print the value of Special_Value you will get 16#1234# (or 0x1234) no matter which endianness the computer implements.
The only time you’ll encounter endianness issues is when you’re copying binary data from one endianness to another, via the network, or a file.
If your debugger gets confused about this, you need a better debugger!
Given below is some code in ada
with TYPE_VECT_B; use TYPE_VECT_B;
Package TEST01 is
procedure TEST01
( In_State : IN VECT_B ;
Out_State : IN OUT VECT_B );
function TEST02
( In_State : IN VECT_B ) return Boolean ;
end TEST01;
The TYPE_VECT_B package specification and body is also defined below
Package TYPE_VECT_B is
type VECT_B is array (INTEGER range <>) OF BOOLEAN ;
rounded_data : float ;
count : integer ;
trace : integer ;
end TYPE_VECT_B;
Package BODY TYPE_VECT_B is
begin
null;
end TYPE_VECT_B;
What does the variable In_State and Out_State actually mean? I think In_State means input variable. I just get confused to what actually Out_State means?
An in parameter can be read but not written by the subprogram. in is the default. Prior to Ada 2012, functions were only allowed to have in parameters. The actual parameter is an expression.
An out parameter implies that the previous value is of no interest. The subprogram is expected to write to the parameter. After writing to the parameter, the subprogram can read back what it has written. On exit the actual parameter receives the value written to it (there are complications in this area!). The actual parameter must be a variable.
An in out parameter is like an out parameter except that the previous value is of interest and can be read by the subprogram before assignment. For example,
procedure Add (V : Integer; To : in out Integer; Limited_To : Integer)
is
begin
-- Check that the result wont be too large. This involves reading
-- the initial value of the 'in out' parameter To, which would be
-- wrong if To was a mere 'out' parameter (it would be
-- uninitialized).
if To + V > Limited_To then
To := Limited_To;
else
To := To + V;
end if;
end Add;
Basically, every parameter to a function or procedure has a direction to it. The options are in, out, in out (both), or access. If you don't see one of those, then it defaults to in.
in means data can go into the subroutine from the caller (via the parameter). You are allowed to read from in parameters inside the routine. out means data can come out of the routine that way, and thus you are allowed to assign values to the parameter inside the routine. In general, how the compiler accomplishes the data passing is up to the compiler, which is in accord with Ada's general philosophy of allowing you to specify what you want done, not how you want it done.
access is a special case, and is roughly like putting a "*" in your parameter definition in Cish languages.
The next question folks usually have is "if I pass something large as an in parameter, is it going to push all that data on the stack or something?" The answer is "no", unless your compiler writers are unconsionably stupid. Every Ada compiler I know of under the hood passes objects larger than fit in a machine register by reference. It is the compiler, not the details of your parameter passing mechanisim, that enforces not writing data back out of the routine. Again, you tell Ada what you want done, it figures out the most efficient way to do it.