Can anyone please make me clear about the use of unchecked conversion in the Ada language.I had tried the pdf and net but all doesn't give me a clear picture to me.
Now I have a small piece of code shown below:
subtype Element4_Range is integer range 1..4;
subtype Element3_Range is integer range 1..3;
subtype Myarr_Range is integer range 1..10;
type Myarr3_Type is array (Myarr_Range) of Element3_Range;
type Myarr4_Type is array (Myarr_Range) of Element4_Range;
Myarr3 : Myarr3_Type;
Myarr4 : Myarr4_Type := (1, 2, 3, 3, 1, 3, 2, 1, 2, 1);
Count_1 : Integer := 0;
Count_2 : Integer := 0;
Count_3 : Integer := 0;
*function To_Myarr3 is new Unchecked_Conversion(Myarr4_type, Myarr3_type);*
Now my doubt here is what does the function Myarr3 exactly do?
An instantiation of Unchecked_Conversion copies the bytes of the source value to the target without checking whether this is sensible. Some compilers will warn (depending maybe on compilation options) if the values are of different sizes.
Element3_Range and Element4_Range are both based on Integer and will use the same number of bytes; so both of your array variables (Myarr3, Myarr4) will need the same number of bytes (40 typically).
You could write
Myarr3 := To_Myarr3 (Myarr4);
As it stands, nothing bad would happen because all the values you've used to initialise Myarr4 are legal as values of Element3_Range.
However, if you had
Myarr3 := To_Myarr3 (Myarr4'(1, 2, 3, 4, others => 1));
you'd end up with Myarr3(4) containing a value outside the legal range of Element3_Range, and with the compiler having no reason to believe that it might not be valid. This may well lead to Constraint_Errors down the line.
You could force a check yourself:
if not Myarr3 (4)'Valid then
-- handle the error case
I had an coworker once who instisted that unchecked_conversion should have been named "Unchcked_Copy" instead. All it does is copy an object of one type into an object of another type.
So your To_Myarr3 routine will accept as a parameter an array of type Myarr4, pretend it is an array of type Myarr3, and then copy every element in it into the left-hand side of your expression.
If you want to change your view of an object from one type to another without copying the whole darn thing around, you can instead use an Unchecked_Conversion on access types for them (so you are only copying the pointer to them). Another method is using for object_name'address use at to overlay one on the other (however, they may both get initialized, which can be bad). But really the best way is to design your system's types so that you never have to use Unchecked_Conversion.
Related
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).
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.
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!
My goal is to call Windows' GetModuleInformation function to get a MODULEINFO struct back. This is all working fine. The problem comes as a result of me wanting to do pointer arithmetic and dereferences on the LPVOID lpBaseOfDll which is part of the MODULEINFO.
Here is my code to call the function in Lua:
require "luarocks.require"
require "alien"
sizeofMODULEINFO = 12 --Gotten from sizeof(MODULEINFO) from Visual Studio
MODULEINFO = alien.defstruct{
{"lpBaseOfDll", "pointer"}; --Does this need to be a buffer? If so, how?
{"SizeOfImage", "ulong"};
{"EntryPoint", "pointer"};
}
local GetModuleInformation = alien.Kernel32.K32GetModuleInformation
GetModuleInformation:types{ret = "int", abi = "stdcall", "long", "pointer", "pointer", "ulong"}
local GetModuleHandle = alien.Kernel32.GetModuleHandleA
GetModuleHandle:types{ret = "pointer", abi = "stdcall", "pointer"}
local GetCurrentProcess = alien.Kernel32.GetCurrentProcess
GetCurrentProcess:types{ret = "long", abi = "stdcall"}
local mod = MODULEINFO:new() --Create struct (needs buffer?)
local currentProcess = GetCurrentProcess()
local moduleHandle = GetModuleHandle("myModule.dll")
local success = GetModuleInformation(currentProcess, moduleHandle, mod(), sizeofMODULEINFO)
if success == 0 then --If there is an error, exit
return 0
end
local dataPtr = mod.lpBaseOfDll
--Now how do I do pointer arithmetic and/or dereference "dataPtr"?
At this point, mod.SizeOfImage seems to be giving me the correct values that I am expecting, so I know the functions are being called and the struct is being populated. However, I cannot seem to do pointer arithmetic on mod.lpBaseOfDll because it is a UserData.
The only information in the Alien Documentation that may address what I'm trying to do are these:
Pointer Unpacking
Alien also provides three convenience functions that let you
dereference a pointer and convert the value to a Lua type:
alien.tostring takes a userdata (usually returned from a function that has a pointer return value), casts it to char*, and returns a Lua
string. You can supply an optional size argument (if you don’t Alien
calls strlen on the buffer first).
alien.toint takes a userdata, casts it to int*, dereferences it and returns it as a number. If you pass it a number it assumes the
userdata is an array with this number of elements.
alien.toshort, alien.tolong, alien.tofloat, and alien.todouble are like alien.toint, but works with with the respective typecasts.
Unsigned versions are also available.
My issue with those, is I would need to go byte-by-byte, and there is no alien.tochar function. Also, and more importantly, this still doesn't solve the problem of me being able to get elements outside of the base address.
Buffers
After making a buffer you can pass it in place of any argument of
string or pointer type.
...
You can also pass a buffer or other userdata to the new method of your
struct type, and in this case this will be the backing store of the
struct instance you are creating. This is useful for unpacking a
foreign struct that a C function returned.
These seem to suggest I can use an alien.buffer as the argument of MODULEINFO's LPVOID lpBaseOfDll. And buffers are described as byte arrays, which can be indexed using this notation: buf[1], buf[2], etc. Additionally, buffers go by bytes, so this would ideally solve all problems. (If I am understanding this correctly).
Unfortunately, I can not find any examples of this anywhere (not in the docs, stackoverflow, Google, etc), so I am have no idea how to do this. I've tried a few variations of syntax, but nearly every one gives a runtime error (others simply does not work as expected).
Any insight on how I might be able to go byte-by-byte (C char-by-char) across the mod.lpBaseOfDll through dereferences and pointer arithmetic?
I need to go byte-by-byte, and there is no alien.tochar function.
Sounds like alien.tostring has you covered:
alien.tostring takes a userdata (usually returned from a function that has a pointer return value), casts it to char*, and returns a Lua string. You can supply an optional size argument (if you don’t Alien calls strlen on the buffer first).
Lua strings can contain arbitrary byte values, including 0 (i.e. they aren't null-terminated like C strings), so as long as you pass a size argument to alien.tostring you can get back data as a byte buffer, aka Lua string, and do whatever you please with the bytes.
It sounds like you can't tell it to start at an arbitrary offset from the given pointer address. The easiest way to tell for sure, if the documentation doesn't tell you, is to look at the source. It would probably be trivial to add an offset parameter.
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.