Search code examples
adamemory-addressgnat

Can a variable passed with 'address attribute to a procedure be modified?


In my test code below, I am trying to modify a variable by passing it as system.address to another procedure.

with Ada.Text_IO;
with System;
with System.Storage_Elements;


procedure Main is
  
  procedure Modify ( Var : in out System.Address) is
    use System.Storage_Elements;
  begin
    Var := Var + 10;
  end Modify;
  
  My_Var : Integer := 10; 
begin
  --  Insert code here.
  Modify (My_Var'Address);
  Ada.Text_IO.Put_Line("My_Var is:" & Integer(My_Var)'Image ); 
  
end Main;

Compiler is returning an error as below,

17:17 actual for "Var" must be a variable

I could not understand the reason as My_Var(actual for Var) is clearly a variable. What should I change to modify My_Var with system.address?

Note: The context of this trail is that I am trying to understand an interface module in an existing legacy project. While there could be better ways to achieve what I need, I want to understand if it is possible to modify a variable with above method.


Solution

  • It would be helpful if you could show the relevant part of the legacy interface module -- it would help us understand what you need and want to do.

    That said, first note that passing a parameter by reference is not usually done in Ada by explicitly passing the 'Address of the actual variable. As you say, there are other and better ways.

    If you pass a System.Address value, and then want to read or write whatever data resides at that address, you have to do the read/write through a variable that you force to have that address, or through an access value (the Ada equivalent of "pointer") that you force to point at that addressed location. In both cases, you are responsible for ensuring that the type of the variable, or of the access value, matches the actual type of the data that you want to read or write.

    To create an access value that points to memory at a given address, you should use the predefined package System.Address_To_Access_Conversions. That requires some understanding of access values and generics, so I won't show an example here.

    To force a variable to have a given address, you declare the variable with the Address aspect set to the given address. The code below shows how that can be done for this example. Note the declaration of the local variable Modify.Var (and note that I changed the name of the parameter from Var to Var_Addr).

    with Ada.Text_IO;
    with System;
    
    procedure Mod_By_Addr is
    
      procedure Modify (Var_Addr : in System.Address) is
        Var : Integer with Address => Var_Addr;
      begin
        Var := Var + 10;
      end Modify;
    
      My_Var : aliased Integer := 10;
    
    begin
      Modify (My_Var'Address);
      Ada.Text_IO.Put_Line("My_Var is:" & Integer(My_Var)'Image );
    
    end Mod_By_Addr;
    

    Since the Var_Addr parameter is not modified in the Modify procedure, it can be declared with the "in" mode, and so the actual parameter can be an expression (My_Var'Address).

    HTH