comp.lang.ada
 help / color / mirror / Atom feed
* Messing with access types...
@ 2020-12-28  9:44 Marek
  2020-12-28 10:14 ` Dmitry A. Kazakov
  0 siblings, 1 reply; 6+ messages in thread
From: Marek @ 2020-12-28  9:44 UTC (permalink / raw)


Hello,

I have some code:

- util.ads:

with System;

package Util is

   type Handle is record
      Str  : access String;
      Data : System.Address;
   end record;

   type Handle_Access is access all Handle;

   Null_Handle : constant Handle := (null, System.Null_Address);

   type Handle_Array is array (Natural range <>) of aliased Handle;

   generic
      type T is private;
      type T_Access is access all T;
   function Get_Data (H : access Handle; Str : access String) return
T_Access;

   generic
      type T is private;
      type T_Access is access all T;
   function Get_Query
     (H        : access Handle; Str : access String; Data : in out T_Access;
      Required : Boolean) return access String;

end Util;

- util.adb

pragma Ada_2012;

with Interfaces.C.Pointers;

with System.Address_To_Access_Conversions;

package body Util is

   package Ptr is new Interfaces.C.Pointers
     (Index              => Natural,
      Element            => Handle,
      Element_Array      => Handle_Array,
      Default_Terminator => Null_Handle);

   use Ptr;

   --------------
   -- Get_Data --
   --------------

   function Get_Data (H : access Handle; Str : access String) return
T_Access
   is
      Pointer : Ptr.Pointer := Ptr.Pointer (H);

      package ATA is new System.Address_To_Access_Conversions (T);

   begin
      if H /= null then
         loop
            declare
               F : access Handle := Pointer;
            begin
               if Str.all = F.Str.all then
                  return T_Access (ATA.To_Pointer (F.Data));
               end if;
            end;

            Ptr.Increment (Pointer);

            exit when Pointer = null;

         end loop;
      end if;

      return null;
   end Get_Data;

   ---------------
   -- Get_Query --
   ---------------

   function Get_Query
     (H        : access Handle; Str : access String; Data : in out T_Access;
      Required : Boolean) return access String
   is
      function Get is new Get_Data (T, T_Access);
   begin

      Data := Get (H, Str);

      if Required and (Data /= null) then
         return Str;
      end if;

      return null;
   end Get_Query;

end Util;

- test.adb

pragma Ada_2012;

with Util;

procedure Test is
   use Util;

   type Some_Record is record
      Foo : Integer;
   end record;

   type Some_Record_Access is access all Some_Record;

   Test_Record : Some_Record_Access;

   H : access Handle := null;

   Str : access String := new String'("Test");

   function Query is new Get_Query (Some_Record, Some_Record_Access);

   Result : access String := Query (H, Str, Test_Record, False);
begin
   null;
end Test;

When I try to compile test.adb I get some warnings:

Compile
   [Ada]          test.adb
test.adb:20:04: warning: in instantiation at util.adb:34
test.adb:20:04: warning: in instantiation at util.adb:56
test.adb:20:04: warning: accessibility check failure
test.adb:20:04: warning: "Program_Error" will be raised at run time
test.adb:20:04: warning: in instantiation at util.adb:34
test.adb:20:04: warning: in instantiation at util.adb:56
test.adb:20:04: warning: cannot convert local pointer to non-local
access type
test.adb:20:04: warning: Program_Error will be raised at run time
   [Ada]          util.adb
Bind
   [gprbind]      test.bexch
   [Ada]          test.ali
Link
   [link]         test.adb
[2020-12-28 10:30:50] process terminated successfully, elapsed time: 00.92s

I tried also to move every local (to Test procedure) variables to global
scope but result is the same.

What is going on? Can you explain where is the problem?
thanks in advance

Marek

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Messing with access types...
  2020-12-28  9:44 Messing with access types Marek
@ 2020-12-28 10:14 ` Dmitry A. Kazakov
  2020-12-28 11:43   ` Marek
  0 siblings, 1 reply; 6+ messages in thread
From: Dmitry A. Kazakov @ 2020-12-28 10:14 UTC (permalink / raw)


On 2020-12-28 10:44, Marek wrote:

> I have some code:

[...]

> When I try to compile test.adb I get some warnings:
> 
> Compile
>     [Ada]          test.adb
> test.adb:20:04: warning: in instantiation at util.adb:34
> test.adb:20:04: warning: in instantiation at util.adb:56
> test.adb:20:04: warning: accessibility check failure
> test.adb:20:04: warning: "Program_Error" will be raised at run time
> test.adb:20:04: warning: in instantiation at util.adb:34
> test.adb:20:04: warning: in instantiation at util.adb:56
> test.adb:20:04: warning: cannot convert local pointer to non-local
> access type
> test.adb:20:04: warning: Program_Error will be raised at run time
>     [Ada]          util.adb
> Bind
>     [gprbind]      test.bexch
>     [Ada]          test.ali
> Link
>     [link]         test.adb
> [2020-12-28 10:30:50] process terminated successfully, elapsed time: 00.92s
> 
> I tried also to move every local (to Test procedure) variables to global
> scope but result is the same.
> 
> What is going on? Can you explain where is the problem?

What are you trying to achieve? Because the code does not make much 
sense to me.

P.S. If you are trying to do some C bindings flat arrays are easier 
choice than Interfaces.C.Pointers. C has no arrays, but pointers. Ada's 
arrays are never compatible with any pointers, except when flat. 
Therefore using access to String would be wrong in most, if not all, cases.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Messing with access types...
  2020-12-28 10:14 ` Dmitry A. Kazakov
@ 2020-12-28 11:43   ` Marek
  2020-12-28 13:56     ` Dmitry A. Kazakov
  0 siblings, 1 reply; 6+ messages in thread
From: Marek @ 2020-12-28 11:43 UTC (permalink / raw)


On 28.12.2020 11:14, Dmitry A. Kazakov wrote:
> 
> What are you trying to achieve? Because the code does not make much
> sense to me.
> 
> P.S. If you are trying to do some C bindings flat arrays are easier
> choice than Interfaces.C.Pointers. C has no arrays, but pointers. Ada's
> arrays are never compatible with any pointers, except when flat.
> Therefore using access to String would be wrong in most, if not all, cases.
> 

Code is a fragment some plugin framework for host written in C. When
plugin is initialized it receives pointer to host features. Then I can
scan them to find concrete feature (Id) and retrieve data.
Problem is with instantiation generic function Get_Data, more precisely
in line:

   return T_Access (ATA.To_Pointer (F.Data));

OK, some changes to code to clear view:

with System;

package Util is

   type Feature is record
      Id  : Integer;
      Data : System.Address;
   end record;

   type Feature_Access is access all Feature;

   Null_Feature : constant Feature := (0, System.Null_Address);

   type Feature_Array is array (Natural range <>) of aliased Feature;

   generic
      type T is private;
      type T_Access is access all T;
   function Get_Data (H : access Feature; Id : Integer) return T_Access;

   generic
      type T is private;
      type T_Access is access all T;
   function Get_Query
     (H        : access Feature; Id : Integer; Data : in out T_Access;
      Required : Boolean) return Integer;

end Util;

------------------------

pragma Ada_2012;

with Interfaces.C.Pointers;

with System.Address_To_Access_Conversions;

package body Util is

   package Ptr is new Interfaces.C.Pointers
     (Index              => Natural,
      Element            => Feature,
      Element_Array      => Feature_Array,
      Default_Terminator => Null_Feature);

   use Ptr;

   --------------
   -- Get_Data --
   --------------

   function Get_Data (H : access Feature; Id : Integer) return T_Access
   is
      Pointer : Ptr.Pointer := Ptr.Pointer (H);

      package ATA is new System.Address_To_Access_Conversions (T);

   begin
      if H /= null then
         loop
            declare
               F : access Feature := Pointer;
            begin
               if Id = F.Id then
                  return T_Access (ATA.To_Pointer (F.Data));
               end if;
            end;

            Ptr.Increment (Pointer);

            exit when Pointer = null;

         end loop;
      end if;

      return null;
   end Get_Data;

   ---------------
   -- Get_Query --
   ---------------

   function Get_Query
     (H        : access Feature; Id : Integer; Data : in out T_Access;
      Required : Boolean) return Integer
   is
      function Get is new Get_Data (T, T_Access);
   begin

      Data := Get (H, Id);

      if Required and (Data /= null) then
         return Id;
      end if;

      return 0;
   end Get_Query;

end Util;

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Messing with access types...
  2020-12-28 11:43   ` Marek
@ 2020-12-28 13:56     ` Dmitry A. Kazakov
  2020-12-28 18:56       ` Marek
  0 siblings, 1 reply; 6+ messages in thread
From: Dmitry A. Kazakov @ 2020-12-28 13:56 UTC (permalink / raw)


On 2020-12-28 12:43, Marek wrote:
> On 28.12.2020 11:14, Dmitry A. Kazakov wrote:
>>
>> What are you trying to achieve? Because the code does not make much
>> sense to me.
>>
>> P.S. If you are trying to do some C bindings flat arrays are easier
>> choice than Interfaces.C.Pointers. C has no arrays, but pointers. Ada's
>> arrays are never compatible with any pointers, except when flat.
>> Therefore using access to String would be wrong in most, if not all, cases.
> 
> Code is a fragment some plugin framework for host written in C. When
> plugin is initialized it receives pointer to host features. Then I can
> scan them to find concrete feature (Id) and retrieve data.
> Problem is with instantiation generic function Get_Data, more precisely
> in line:
> 
>     return T_Access (ATA.To_Pointer (F.Data));

You should never convert access types, because to do so, you must first 
learn the accessibility rules. No man can do that while keeping sanity. 
So, just do this:

    return ATA.To_Pointer (F.Data).all'Unchecked_Access;
        -- Mind your business, COMPILER!

> OK, some changes to code to clear view:
> 
> with System;
> 
> package Util is
> 
>     type Feature is record
>        Id  : Integer;
>        Data : System.Address;
>     end record;

    type Feature is record
       Id   : Interfaces.C.int; -- If that is int
       Data : System.Address;   -- Could be a named access type [*]
    end record;
    pragma Convention (C, Feature);

>     type Feature_Access is access all Feature;
> 
>     Null_Feature : constant Feature := (0, System.Null_Address);


>     type Feature_Array is array (Natural range <>) of aliased Feature;

    type Feature_Array is array (size_t) of aliased Feature; -- Flat
    pragma Convention (C, Feature_Array);
    type Feature_Array_Ptr is access all Feature_Array;
    pragma Convention (C, Feature_Array_Ptr);

    function Get_Me_Features return Feature_Array_Ptr;
    pragma Import (C, Get_Me_features, "???");

That should be all necessary to do this:

    List : Feature_Array renames Get_Me_Features.all;
begin
    for I in size_t'Range loop
       exit when List (I) = Null_Feature;
       declare
          Data : Whatever_C_Type;
          for Data'Address use List (I).Data;
          pragma Import (Ada, Data);
       begin
          ...
       end;
    end loop;

-------------------------------------
* Here is a variant without using addresses

    type Whatever_C_Type is record
       ...
    end record;
    pragma Convention (C, Whatever_C_Type);
    type Whatever_C_Type_Ptr is access all Whatever_C_Type;
    pragma Convention (C, Whatever_C_Type_Ptr);

    type Feature is record
       Id   : Interfaces.C.int;
       Data : Whatever_Ptr;
    end record;
    pragma Convention (C, Feature);
    ...

    for I in size_t'Range loop
       exit when List (I) = Null_Feature;
       declare
          Data : Whatever_C_Type renames List (I).Data.all;
       begin
          ...
       end;
    end loop;

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Messing with access types...
  2020-12-28 13:56     ` Dmitry A. Kazakov
@ 2020-12-28 18:56       ` Marek
  2020-12-28 19:53         ` Dmitry A. Kazakov
  0 siblings, 1 reply; 6+ messages in thread
From: Marek @ 2020-12-28 18:56 UTC (permalink / raw)


Thanks a lot Dmitry for your time.

Now I got everything working. But... I see a lot of mess by trying to
play with pointers <-> access types. Then my plugin code looks like the
work of Frankenstein.
I think I'll try another approach: just write extra layer in C, which
will direct communicate with host and will provide prepared data for Ada
part.

regards
Marek

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Messing with access types...
  2020-12-28 18:56       ` Marek
@ 2020-12-28 19:53         ` Dmitry A. Kazakov
  0 siblings, 0 replies; 6+ messages in thread
From: Dmitry A. Kazakov @ 2020-12-28 19:53 UTC (permalink / raw)


On 2020-12-28 19:56, Marek wrote:

> I think I'll try another approach: just write extra layer in C, which
> will direct communicate with host and will provide prepared data for Ada
> part.

The standard approach is somewhat different:

1. Thin Ada bindings mapping to C, mostly 1-to-1 with minor adjustments, 
e.g.

- replacing BOOL with Ada Boolean
- translating C strings into Ada Strings
- replacing C pointer parameters with Ada out or in out, as appropriate
- making proper functions where C cannot, e.g. returning string results 
where C API uses a buffer to store string copy. The buffer is made 
local, C function is called to fill it. Then the contents is converted 
to Ada string and returned out.

2. On top of that you do thick Ada bindings:

- reasonable interfaces
- all implementation types made private
- return codes replaced with exceptions
- C's malloc/free pairs wrapped by Ada controlled types
- similarly resources seizing/releasing pairs go into controlled types
- messages/events loops encapsulated into Ada tasks
- these tasks are wrapped by a controlled type (AKA active object)

You do not need to change anything on the C side. You can incrementally 
advance #2 as far as you feel necessary since you can always fall back 
to #1.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2020-12-28 19:53 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-28  9:44 Messing with access types Marek
2020-12-28 10:14 ` Dmitry A. Kazakov
2020-12-28 11:43   ` Marek
2020-12-28 13:56     ` Dmitry A. Kazakov
2020-12-28 18:56       ` Marek
2020-12-28 19:53         ` Dmitry A. Kazakov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox