* 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 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 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
* 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: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
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