1

I need to pass a structure from C to Ada in an Ada binding application. Thus, I have declared the structure in both Ada and C so that both the source sides can decipher the structure composition.

In C,

typedef struct {
   int Status_Code;
   int Error_Code;
} Edit_Result_Type;

In Ada,

type Edit_Result_Rec_Type is
   record
      Status     : Integer;
      Error_Code : Integer;
   end record;

pragma Convention (Convention => C, 
                   Entity     => Edit_Result_Rec_Type);

for Edit_Result_Rec_Type use
  record
    Status     at 0 range  0 .. 31;
    Error_Code at 0 range 32 .. 63;
  end record;

type Edit_Result_Rec_Type_Ptr is access all Edit_Result_Rec_Type;

When I am passing the structure from C to Ada through call by reference, I wanted to know:

  1. Is it ok if I declare an "access all" vector type for the structure (as done above - Edit_Result_Rec_Type_Ptr) in Ada and use it directly as a formal parameter in Ada function. For Eg:

    procedure Process_Data (Edit_Result_Ptr : in out Edit_Result_Rec_Type_Ptr) is
    begin
       Edit_Result_Ptr.Status     := 1;
       Edit_Result_Ptr.Error_Code := 0;
    end Process_Data;
    

    Is this approach FATAL? I know it is, just wanted to know "how" in depth!

  2. Any other (better) approaches for passing through call by reference? I believe I can pass it as a "System.Address" parameter and do an "unchecked conversion" into Edit_Result_Rec_Type_Ptr in a local vector inside the Ada function and then read/write record members? Does this approach has any fatalities?

Akay
  • 1,092
  • 12
  • 32

1 Answers1

6

When interfacing Ada and C, you should really read the RM, Annex B.3, which says:

An Ada parameter of a record type T, of any mode, other than an in parameter of a type of convention C_Pass_By_Copy, is passed as a t* argument to a C function, where t is the C struct corresponding to the Ada type T.

So, in your procedure, just do:

procedure Process_Data (Edit_Result : in out Edit_Result_Rec_Type) is
begin
   Edit_Result.Status     := 1;
   Edit_Result.Error_Code := 0;
end Process_Data;

and

pragma Export(C, Process_Data);

(or use the aspect, if Ada 2012)

That being said, you shouldn't use Integer in your record definition, Interfaces.C.int is the way to go:

type Edit_Result_Rec_Type is
   record
      Status     : Interfaces.C.int;
      Error_Code : Interfaces.C.int;
   end record;

which will match the C int on your platform (assuming your C compiler is compatible with the Ada compiler)

As for your questions:

  1. That would work, but why mess around with pointers?

  2. No, that wouldn't work, access types (and access values) in Ada are not addresses. Converting a System.Address to an access value requires System.Address_To_Access_Conversions, but again, why mess around with pointers?

egilhh
  • 6,464
  • 1
  • 18
  • 19
  • So when you say to use the procedure with the signature as: `procedure Process_Data (Edit_Result : in out Edit_Result_Rec_Type) is`. Should I be passing the address of the struct variable from c or the struct variable itself? – Akay Mar 25 '16 at 22:11
  • 1
    the C definition should be t* – egilhh Mar 25 '16 at 22:15
  • Thanks!, Can you also comment on the safety aspect of using your approach (that you gave in the answer)? And whether if it does have any fatalities? And how is it unsafe/dodgy if I take a System.Address as an **in** parameter in the function and do an **unchecked conversion** into access of **Edit_Result_Rec_Type** because I tried this approach as well and it worked! – Akay Mar 27 '16 at 11:40
  • 1
    The ARM reference is about calling C from Ada, but OP wants to call Ada from C. GNAT may do what you want, but the code may not be portable. Consider `access` instead of `in out`? – Simon Wright Nov 13 '17 at 08:30