comp.lang.ada
 help / color / mirror / Atom feed
* Convert between C "void*" pointer and an access
@ 2017-10-11 21:52 Victor Porton
  2017-10-11 22:32 ` Randy Brukardt
  2017-10-11 22:58 ` Victor Porton
  0 siblings, 2 replies; 9+ messages in thread
From: Victor Porton @ 2017-10-11 21:52 UTC (permalink / raw)


What is the right way to convert between C "void*" pointer and an access to 
a tagged or class-wide type?

Ada.Unchecked_Conversion seems to be what I need, but the access type may be 
"fat" and thus have another format than void*.

I caught myself doing such unchecked conversions, but now I feel I did an 
error.

For example, let

type X_Type is abstract tagged null record;

How to store X_Type'Class pointer in "void*" and convert them back and 
forth?

-- 
Victor Porton - http://portonvictor.org


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

* Re: Convert between C "void*" pointer and an access
  2017-10-11 21:52 Convert between C "void*" pointer and an access Victor Porton
@ 2017-10-11 22:32 ` Randy Brukardt
  2017-10-11 23:03   ` Victor Porton
  2017-10-29 14:50   ` David Thompson
  2017-10-11 22:58 ` Victor Porton
  1 sibling, 2 replies; 9+ messages in thread
From: Randy Brukardt @ 2017-10-11 22:32 UTC (permalink / raw)


"Victor Porton" <porton@narod.ru> wrote in message 
news:orm3qe$1r0c$1@gioia.aioe.org...
> What is the right way to convert between C "void*" pointer and an access 
> to
> a tagged or class-wide type?

I doubt that there is a *right* way, there just are several possibilities.

> Ada.Unchecked_Conversion seems to be what I need, but the access type may 
> be
> "fat" and thus have another format than void*.

Right, but not very likely. I'd expect this to work in most cases.

But our solution in the Claw libraries was simply to use the appropriate 
C-convention access type in the interface definitions and avoid making any 
conversions at all. I believe that on the C side, void* and <anything-else>* 
have to use the same representation, so C convention should work properly 
for any pointer type.

That is, if the C interface contains a void* parameter, we just used an 
appropriate C convention access type in its place in the Ada parameter 
definition. (Using overloading if we needed multiple such pointers - but 
that was very rare.) There's no counterpart to void* in Ada anyway, so one 
has to do something like that in the interfacing definitions.

                                                Randy.


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

* Re: Convert between C "void*" pointer and an access
  2017-10-11 21:52 Convert between C "void*" pointer and an access Victor Porton
  2017-10-11 22:32 ` Randy Brukardt
@ 2017-10-11 22:58 ` Victor Porton
  2017-10-11 23:12   ` Victor Porton
  1 sibling, 1 reply; 9+ messages in thread
From: Victor Porton @ 2017-10-11 22:58 UTC (permalink / raw)


Victor Porton wrote:

> What is the right way to convert between C "void*" pointer and an access
> to a tagged or class-wide type?
> 
> Ada.Unchecked_Conversion seems to be what I need, but the access type may
> be "fat" and thus have another format than void*.
> 
> I caught myself doing such unchecked conversions, but now I feel I did an
> error.
> 
> For example, let
> 
> type X_Type is abstract tagged null record;
> 
> How to store X_Type'Class pointer in "void*" and convert them back and
> forth?

It seems I've found a right solution of my problem. (Check if Ada RM
warrants that it works with every compiler!)

Well, why it is not a standard package?! Why I need to invent something
"smart" every time I need to code?

with System.Address_To_Access_Conversions;
with Interfaces.C.Strings; use Interfaces.C.Strings;

-- C11, 6.2.5, 28 (draft N1548):
-- A pointer to void shall have the same representation and alignment requirements as a pointer to a character type.
-- So we can use chars_ptr to mean void pointer in C.

generic
    type Object(<>) is limited private;
package Convert_Void is

   package Address_Conversions is new System.Address_To_Access_Conversions(Object);

   subtype Object_Pointer is Address_Conversions.Object_Pointer;

   function To_Access (Void_Pointer: chars_ptr) return Object_Pointer;

   function To_C_Pointer (Pointer: Object_Pointer) return chars_ptr;

end Convert_Void;


with Ada.Unchecked_Conversion;

package body Convert_Void is

   type My_Char_Pointer is access all char with Convention=>C;

   function From_My_Char_Pointer is new Ada.Unchecked_Conversion(My_Char_Pointer, chars_ptr);
   function From_Chars_Ptr is new Ada.Unchecked_Conversion(chars_ptr, My_Char_Pointer);

   package Char_Address_Conversions is new System.Address_To_Access_Conversions(char);

   function To_Access (Void_Pointer: chars_ptr) return Object_Pointer is
   begin
      return Address_Conversions.To_Pointer(From_Chars_Ptr(Void_Pointer)'Address);
   end;

   function To_C_Pointer (Pointer: Object_Pointer) return chars_ptr is
      A: constant System.Address := Address_Conversions.To_Address(Pointer);
      P: constant My_Char_Pointer :=
        My_Char_Pointer(Char_Address_Conversions.To_Pointer(A));
   begin
      return From_My_Char_Pointer(P);
   end;

end Convert_Void;

-- 
Victor Porton - http://portonvictor.org

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

* Re: Convert between C "void*" pointer and an access
  2017-10-11 22:32 ` Randy Brukardt
@ 2017-10-11 23:03   ` Victor Porton
  2017-10-12  7:57     ` Dmitry A. Kazakov
  2017-10-12  8:05     ` Simon Wright
  2017-10-29 14:50   ` David Thompson
  1 sibling, 2 replies; 9+ messages in thread
From: Victor Porton @ 2017-10-11 23:03 UTC (permalink / raw)


Randy Brukardt wrote:

> "Victor Porton" <porton@narod.ru> wrote in message
> news:orm3qe$1r0c$1@gioia.aioe.org...
>> What is the right way to convert between C "void*" pointer and an access
>> to
>> a tagged or class-wide type?
> 
> I doubt that there is a *right* way, there just are several possibilities.
> 
>> Ada.Unchecked_Conversion seems to be what I need, but the access type may
>> be
>> "fat" and thus have another format than void*.
> 
> Right, but not very likely. I'd expect this to work in most cases.
> 
> But our solution in the Claw libraries was simply to use the appropriate
> C-convention access type in the interface definitions and avoid making any
> conversions at all. I believe that on the C side, void* and
> <anything-else>* have to use the same representation, so C convention
> should work properly for any pointer type.
> 
> That is, if the C interface contains a void* parameter, we just used an
> appropriate C convention access type in its place in the Ada parameter
> definition. (Using overloading if we needed multiple such pointers - but
> that was very rare.) There's no counterpart to void* in Ada anyway, so one
> has to do something like that in the interfacing definitions.

"- What is your solution for Windows printing problems? - Do not print."

I NEED to convert between access to tagged records (and access to class-wide 
types) and C void* pointers.

I need this because it is C's way to pass objects by using void* and I need 
to pass Ada objects to C functions (not just C objects).

See my other message where I developed a package which is likely to work 
with every Ada compiler (or hopefully even with every conforming Ada 
compiler possible).

-- 
Victor Porton - http://portonvictor.org


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

* Re: Convert between C "void*" pointer and an access
  2017-10-11 22:58 ` Victor Porton
@ 2017-10-11 23:12   ` Victor Porton
  2017-10-12  1:01     ` Victor Porton
  0 siblings, 1 reply; 9+ messages in thread
From: Victor Porton @ 2017-10-11 23:12 UTC (permalink / raw)


Victor Porton wrote:

> Victor Porton wrote:
> 
>> What is the right way to convert between C "void*" pointer and an access
>> to a tagged or class-wide type?
>> 
>> Ada.Unchecked_Conversion seems to be what I need, but the access type may
>> be "fat" and thus have another format than void*.
>> 
>> I caught myself doing such unchecked conversions, but now I feel I did an
>> error.
>> 
>> For example, let
>> 
>> type X_Type is abstract tagged null record;
>> 
>> How to store X_Type'Class pointer in "void*" and convert them back and
>> forth?
> 
> It seems I've found a right solution of my problem. (Check if Ada RM
> warrants that it works with every compiler!)
> 
> Well, why it is not a standard package?! Why I need to invent something
> "smart" every time I need to code?

The package body was with a bug. Here there is corrected code:


with System.Address_To_Access_Conversions;
with Interfaces.C.Strings; use Interfaces.C.Strings;

-- C11, 6.2.5, 28 (draft N1548):
-- A pointer to void shall have the same representation and alignment requirements as a pointer to a character type.
-- So we can use chars_ptr to mean void pointer in C.

generic
    type Object(<>) is limited private;
package Convert_Void is

   package Address_Conversions is new System.Address_To_Access_Conversions(Object);

   subtype Object_Pointer is Address_Conversions.Object_Pointer;

   function To_Access (Void_Pointer: chars_ptr) return Object_Pointer;

   function To_C_Pointer (Pointer: Object_Pointer) return chars_ptr;

end Convert_Void;


with Ada.Unchecked_Conversion;

package body Convert_Void is

   type My_Char_Pointer is access all char with Convention=>C;

   function From_My_Char_Pointer is new Ada.Unchecked_Conversion(My_Char_Pointer, chars_ptr);
   function From_Chars_Ptr is new Ada.Unchecked_Conversion(chars_ptr, My_Char_Pointer);

   package Char_Address_Conversions is new System.Address_To_Access_Conversions(char);

   function To_Access (Void_Pointer: chars_ptr) return Object_Pointer is
      P: constant My_Char_Pointer := From_Chars_Ptr(Void_Pointer);
      A: constant System.Address :=
        Char_Address_Conversions.To_Address(Char_Address_Conversions.Object_Pointer(P));
   begin
      return Address_Conversions.To_Pointer(A);
   end;

   function To_C_Pointer (Pointer: Object_Pointer) return chars_ptr is
      A: constant System.Address := Address_Conversions.To_Address(Pointer);
      P: constant My_Char_Pointer :=
        My_Char_Pointer(Char_Address_Conversions.To_Pointer(A));
   begin
      return From_My_Char_Pointer(P);
   end;

end Convert_Void;

-- 
Victor Porton - http://portonvictor.org


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

* Re: Convert between C "void*" pointer and an access
  2017-10-11 23:12   ` Victor Porton
@ 2017-10-12  1:01     ` Victor Porton
  0 siblings, 0 replies; 9+ messages in thread
From: Victor Porton @ 2017-10-12  1:01 UTC (permalink / raw)


Victor Porton wrote:

> Victor Porton wrote:
> 
>> Victor Porton wrote:
>> 
>>> What is the right way to convert between C "void*" pointer and an access
>>> to a tagged or class-wide type?
>>> 
>>> Ada.Unchecked_Conversion seems to be what I need, but the access type
>>> may be "fat" and thus have another format than void*.
>>> 
>>> I caught myself doing such unchecked conversions, but now I feel I did
>>> an error.
>>> 
>>> For example, let
>>> 
>>> type X_Type is abstract tagged null record;
>>> 
>>> How to store X_Type'Class pointer in "void*" and convert them back and
>>> forth?
>> 
>> It seems I've found a right solution of my problem. (Check if Ada RM
>> warrants that it works with every compiler!)
>> 
>> Well, why it is not a standard package?! Why I need to invent something
>> "smart" every time I need to code?
> 
> The package body was with a bug. Here there is corrected code:

[snip] (see code above in the thread)

Now I will prove that it works without errors with every conforming 
compilers.

The below will describe how my package Convert_Void works. If in doubt, 
consult the package source code.

First I should formulate the problem exactly:

Having an Ada object (we can restrict to tagged and class-wide types and 
benefit from the fact that such objects 'Address "should produce a useful 
result" (RM 13.3.16)), we need to transform it to C void pointer and pass to 
C code; the C code should be able to call Ada code (e.g. through subprogram 
access) and pass the pointer back and the Ada code should be able to work 
with the original Ada object.

In other words, we need to define two functions: To_Pointer which converts 
all access values for a certain type (tagged or class-wide, at least) into 
void pointers and To_Access which converts void pointers obtained by 
To_Pointer back into the original access value.

In other words, we need two mutually inverse bijections between every set of 
values of 'Unchecked_Access of values of a type (tagged or class-wide, at 
least) and a subset of C void pointers.

We also need map null access to NULL pointer and vice versa.

We will use chars_ptr from Interfaces.C.Strings to represent void pointers. 
It is valid because C11, 6.2.5, 28 (draft N1548): "A pointer to void shall 
have the same representation and alignment requirements as a pointer to a 
character type."

This problem is solved in my generic package Convert_Void:

-- "with" and "use" skipped
generic
    type Object(<>) is limited private;
package Convert_Void is

   package Address_Conversions is new
     System.Address_To_Access_Conversions(Object);
   
   subtype Object_Pointer is Address_Conversions.Object_Pointer;
   
   function To_Access (Void_Pointer: chars_ptr) return Object_Pointer;
   
   function To_C_Pointer (Pointer: Object_Pointer) return chars_ptr;
   
end Convert_Void;

We define in Convert_Void body:

type My_Char_Pointer is access all char with Convention=>C;
package Char_Address_Conversions is new
  System.Address_To_Access_Conversions(char);

One of the steps of implementing the functions To_Pointer and To_Access is 
to convert between chars_ptr and My_Char_Pointer. This is trivially done 
with Ada.Unchecked_Conversion because they are by definition the same C type 
char* and thus have the same representation.

So our task is reduced to conversion between My_Char_Pointer an Ada object 
access.

We do this conversion (in both directions) like:

Char_Address_Conversions.To_Pointer(Address_Conversions.To_Address(...))

Address_Conversions.To_Pointer(Char_Address_Conversions.To_Address(...))

The thing to prove is that this is an injective function from object 
pointers to My_Char_Pointer values and that backward conversion is really 
its inversion.

But it is just my understanding of "The To_Pointer and To_Address 
subprograms convert back and forth between values of types Object_Pointer 
and Address." (RM 13.7.2 5/2)

That null access are mapped into NULL pointers and vice versa is left as an 
exercise to the reader.

-- 
Victor Porton - http://portonvictor.org

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

* Re: Convert between C "void*" pointer and an access
  2017-10-11 23:03   ` Victor Porton
@ 2017-10-12  7:57     ` Dmitry A. Kazakov
  2017-10-12  8:05     ` Simon Wright
  1 sibling, 0 replies; 9+ messages in thread
From: Dmitry A. Kazakov @ 2017-10-12  7:57 UTC (permalink / raw)


On 12/10/2017 01:03, Victor Porton wrote:
> Randy Brukardt wrote:
> 
>> "Victor Porton" <porton@narod.ru> wrote in message
>> news:orm3qe$1r0c$1@gioia.aioe.org...
>>> What is the right way to convert between C "void*" pointer and an access
>>> to
>>> a tagged or class-wide type?
>>
>> I doubt that there is a *right* way, there just are several possibilities.
>>
>>> Ada.Unchecked_Conversion seems to be what I need, but the access type may
>>> be
>>> "fat" and thus have another format than void*.
>>
>> Right, but not very likely. I'd expect this to work in most cases.
>>
>> But our solution in the Claw libraries was simply to use the appropriate
>> C-convention access type in the interface definitions and avoid making any
>> conversions at all. I believe that on the C side, void* and
>> <anything-else>* have to use the same representation, so C convention
>> should work properly for any pointer type.
>>
>> That is, if the C interface contains a void* parameter, we just used an
>> appropriate C convention access type in its place in the Ada parameter
>> definition. (Using overloading if we needed multiple such pointers - but
>> that was very rare.) There's no counterpart to void* in Ada anyway, so one
>> has to do something like that in the interfacing definitions.
> 
> "- What is your solution for Windows printing problems? - Do not print."
> 
> I NEED to convert between access to tagged records (and access to class-wide
> types) and C void* pointers.
> 
> I need this because it is C's way to pass objects by using void* and I need
> to pass Ada objects to C functions (not just C objects).

What Randy says is that in many cases you can pass an Ada access type 
where void * is expected:

    type T is tagged whatever ...;
--
-- void c_foo (void * userdata);
--
    type T_Ptr is access all T['Class];
    pragma Convention (C, T_Ptr);

    procedure Set_Callback (..., User_Data : T_Ptr);
    pragma Import (C, Set_Callback, "...");

This is the most safe and clean way to pass pointers to Ada objects 
through C library, assuming that C does not touch the object.

In other cases you can simply declare it System.Address and use 
Access_To_Address conversion or the Address pragma/aspect or fake pool 
allocator new. Whatever you like.

System.Address is frowned at because it not guaranteed to work 
everywhere, but chances are high that if that does not work nothing else 
would either. So I would not worry too much about it.

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

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

* Re: Convert between C "void*" pointer and an access
  2017-10-11 23:03   ` Victor Porton
  2017-10-12  7:57     ` Dmitry A. Kazakov
@ 2017-10-12  8:05     ` Simon Wright
  1 sibling, 0 replies; 9+ messages in thread
From: Simon Wright @ 2017-10-12  8:05 UTC (permalink / raw)


Victor Porton <porton@narod.ru> writes:

> I NEED to convert between access to tagged records (and access to
> class-wide types) and C void* pointers.
>
> I need this because it is C's way to pass objects by using void* and I
> need to pass Ada objects to C functions (not just C objects).

What we think you need to do is to pass a class-wide pointer to a C
function so that it can do something with it.

One would hope that you could say e.g.

      type T is tagged null record;
      type T_P is access all T'Class with Convention => C;
      procedure Put (Obj : T_P)                 -- <<< warning here
      with
        Import,
        Convention => C,
        External_Name => "demo_put";
      function Get return T_P                   -- <<< warning here
      with
        Import,
        Convention => C,
        External_Name => "demo_get";
      type U is new T with null record;
      O : aliased U;
   begin
      Put (O'Access);
      declare
         Ret : constant T_P := Get;
      begin
         Put_Line ("ret's class is " & Ada.Tags.Expanded_Name (Ret'Tag));
      end;

and that the compiler would refuse to compile the code if it couldn't
map the type's conventions as specified.

Trying this on macOS results in warnings

   class_pointers.adb:6:19: warning: "Put.Obj" involves a tagged type which
    does not correspond to any C type
   class_pointers.adb:11:13: warning: return type of "Get" does not
    correspond to C type

but the resulting executable works as expected. (You would have expected
a similar warning on the type definition; perhaps this is a bug?)

In the past I've had success by specifying the size of the classwide
type (this doesn't suppress the warnings).


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

* Re: Convert between C "void*" pointer and an access
  2017-10-11 22:32 ` Randy Brukardt
  2017-10-11 23:03   ` Victor Porton
@ 2017-10-29 14:50   ` David Thompson
  1 sibling, 0 replies; 9+ messages in thread
From: David Thompson @ 2017-10-29 14:50 UTC (permalink / raw)


On Wed, 11 Oct 2017 17:32:35 -0500, "Randy Brukardt"
<randy@rrsoftware.com> wrote:

> "Victor Porton" <porton@narod.ru> wrote in message 
> news:orm3qe$1r0c$1@gioia.aioe.org...
> > What is the right way to convert between C "void*" pointer and an access 
> > to
> > a tagged or class-wide type?
> 
> I doubt that there is a *right* way, there just are several possibilities.
> 
> > Ada.Unchecked_Conversion seems to be what I need, but the access type may 
> > be
> > "fat" and thus have another format than void*.
> 
> Right, but not very likely. I'd expect this to work in most cases.
> 
> But our solution in the Claw libraries was simply to use the appropriate 
> C-convention access type in the interface definitions and avoid making any 
> conversions at all. I believe that on the C side, void* and <anything-else>* 
> have to use the same representation, so C convention should work properly 
> for any pointer type.
> 
The C standard doesn't require that (now or ever). It requires that 

- void* and [signed|unsigned] char* (each with or without qualifers =
const, void) must have the same representation, probably because in
K&R1 C, before C89 introduced void, char* was used for many things
that void* (or qualified void*) is now used for.

- pointers to all structs must do the same, as must all unions, mostly
because you can 'forward declare' a struct or union and point to it
before its contents (and thus size/alignment) are known. Notably this
is NOT required for enums, probably because enum types themselves are
not required to have the same representation, and _are_ required to be
fully defined before use -- although enum _literals_ in C are all type
'signed int' regardless of the enum type; this is different in C++.

Except for those, pointer types are allowed to differ. 

Nowadays most hardware is byte-addressed, so in practice all data
pointers _are_ the same. Code (function) pointers are sometimes
different, but not often, and never in POSIX. It wouldn't surprise me
if every Ada implementation/target that _has_ C interop pairs to a C
implementation with at least all data pointer types the same.

However, last century I worked on one system with a pretty strong
preference for word addressing, and it did have void* char* different
from int* float* struct* etc, and when I looked at the code generated
it had shifts to convert back and forth all over the place.

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

end of thread, other threads:[~2017-10-29 14:50 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-10-11 21:52 Convert between C "void*" pointer and an access Victor Porton
2017-10-11 22:32 ` Randy Brukardt
2017-10-11 23:03   ` Victor Porton
2017-10-12  7:57     ` Dmitry A. Kazakov
2017-10-12  8:05     ` Simon Wright
2017-10-29 14:50   ` David Thompson
2017-10-11 22:58 ` Victor Porton
2017-10-11 23:12   ` Victor Porton
2017-10-12  1:01     ` Victor Porton

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