comp.lang.ada
 help / color / mirror / Atom feed
* Interfacing to C without dynamic memory
@ 2008-11-14 13:58 Maciej Sobczak
  2008-11-14 20:35 ` Damien Carbonne
  2008-11-14 23:13 ` Robert A Duff
  0 siblings, 2 replies; 6+ messages in thread
From: Maciej Sobczak @ 2008-11-14 13:58 UTC (permalink / raw)


Hi,

Consider a C (or C++) library that defines some type T with some
functions operating on it.
The type T can be a complex type, encapsulated in a struct or class.
Assuming extern "C" interface, the library functions usually accept a
pointer to T:

struct T
{
    // lots of interesting stuff
};

void createT(T * object);
void destroyT(T * object);
void foo(T * object, int something);
void bar(T * object, int something_else);

In C and C++ it is possible to use objects of type T without
allocating them dynamically, which would be otherwise mandated by an
alternative interface:

T * newT();
void deleteT(T * object);

How would you approach wrapping such a library for Ada while retaining
the requirement that dynamic memory is not obligatory (ie. with the
original interface above)?

My first ideas are:
1. Extract sizeof(T) at C level.
2. In Ada, create appropriately aligned:

type T is System.Storage_Array (1 .. Size_Of_T);
for T'Alignment use Appropriate_Alignment_Value;

3. Wrap imported C functions by passing to them the 'Address of T's
instances.

This way, users will be able to use T as any other value type, without
resorting to dynamic memory.

Does it make sense? Is there any better way?

--
Maciej Sobczak * www.msobczak.com * www.inspirel.com

Database Access Library for Ada: www.inspirel.com/soci-ada



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

* Re: Interfacing to C without dynamic memory
  2008-11-14 13:58 Interfacing to C without dynamic memory Maciej Sobczak
@ 2008-11-14 20:35 ` Damien Carbonne
  2008-11-15  1:12   ` Randy Brukardt
  2008-11-14 23:13 ` Robert A Duff
  1 sibling, 1 reply; 6+ messages in thread
From: Damien Carbonne @ 2008-11-14 20:35 UTC (permalink / raw)


Hi,

I have never tried what you propose, but I think it should work.
Of course, all fields of T should be initialized in C code (in createT, 
I guess).

In Ada, you could also wrap the array in a record.
That way, you could initialize everything to 0:

type T is record
    Bytes :  System.Storage_Array (...);
end record;
pragma Convention (C, T);
+ other pragma if necessary
...

Make T private, or even limited private: Ada users should not have 
direct access to T fields.
Also, you shouldn't need to use 'Address.
By default, when you use C convention/import, structures and arrays are 
passed by address.

Of course, as you don't have direct access to T fields, you need to 
provide all necessary functions to do that.

So you would have:

package XXX is
    type T is [limited] private;

    procedure Create (O : in out T);
    procedure Destroy (O : in out T);
    ...

private
    type T is ... -- as your proposal
    ...

    pragma Import (C, Create, "CreateT");
    pragma Import (C, Destroy, "DestroyT");
    ...
end XXX;


Maciej Sobczak a �crit :
> Hi,
> 
> Consider a C (or C++) library that defines some type T with some
> functions operating on it.
> The type T can be a complex type, encapsulated in a struct or class.
> Assuming extern "C" interface, the library functions usually accept a
> pointer to T:
> 
> struct T
> {
>     // lots of interesting stuff
> };
> 
> void createT(T * object);
> void destroyT(T * object);
> void foo(T * object, int something);
> void bar(T * object, int something_else);
> 
> In C and C++ it is possible to use objects of type T without
> allocating them dynamically, which would be otherwise mandated by an
> alternative interface:
> 
> T * newT();
> void deleteT(T * object);
> 
> How would you approach wrapping such a library for Ada while retaining
> the requirement that dynamic memory is not obligatory (ie. with the
> original interface above)?
> 
> My first ideas are:
> 1. Extract sizeof(T) at C level.
> 2. In Ada, create appropriately aligned:
> 
> type T is System.Storage_Array (1 .. Size_Of_T);
> for T'Alignment use Appropriate_Alignment_Value;
> 
> 3. Wrap imported C functions by passing to them the 'Address of T's
> instances.
> 
> This way, users will be able to use T as any other value type, without
> resorting to dynamic memory.
> 
> Does it make sense? Is there any better way?
> 
> --
> Maciej Sobczak * www.msobczak.com * www.inspirel.com
> 
> Database Access Library for Ada: www.inspirel.com/soci-ada



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

* Re: Interfacing to C without dynamic memory
  2008-11-14 13:58 Interfacing to C without dynamic memory Maciej Sobczak
  2008-11-14 20:35 ` Damien Carbonne
@ 2008-11-14 23:13 ` Robert A Duff
  2008-11-15 11:52   ` Samuel Tardieu
  1 sibling, 1 reply; 6+ messages in thread
From: Robert A Duff @ 2008-11-14 23:13 UTC (permalink / raw)


Maciej Sobczak <see.my.homepage@gmail.com> writes:

> My first ideas are:
> 1. Extract sizeof(T) at C level.
> 2. In Ada, create appropriately aligned:
>
> type T is System.Storage_Array (1 .. Size_Of_T);
> for T'Alignment use Appropriate_Alignment_Value;

Makes sense.  You could write a C program that #include's
the relevant .h file, and prints out an Ada package
spec containing the above Ada code.  You could run
this as part of your build scripts.

- Bob



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

* Re: Interfacing to C without dynamic memory
  2008-11-14 20:35 ` Damien Carbonne
@ 2008-11-15  1:12   ` Randy Brukardt
  0 siblings, 0 replies; 6+ messages in thread
From: Randy Brukardt @ 2008-11-15  1:12 UTC (permalink / raw)


...
> 3. Wrap imported C functions by passing to them the 'Address of T's
> instances.

You were fine up to here. But there is no reason at all to do this; you can 
use 'Access and a general access type with the appropriate convention; 
'Access and an anonymous access type, or best of all, just plain parameters 
(which will be passed by reference for routines with the C convention).

An Ada 95 or newer program that uses System.Address outside of address 
clauses is broken, IMHO. There are many safer ways to do those things. (And 
avoiding access types is good, too.)

For Claw, most of the interfaces use locally declared records with the 
StdCall convention (and all of the components defined by Win32), and usually 
normal 'in' and 'in out' parameters. The only access parameters that we used 
was in functions that modified some of their arguments (and that was only 
because of Ada's broken rule against 'in out' parameters in functions).

                                    Randy.

                                  Randy.





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

* Re: Interfacing to C without dynamic memory
  2008-11-14 23:13 ` Robert A Duff
@ 2008-11-15 11:52   ` Samuel Tardieu
  2008-11-16 21:31     ` Maciej Sobczak
  0 siblings, 1 reply; 6+ messages in thread
From: Samuel Tardieu @ 2008-11-15 11:52 UTC (permalink / raw)


>>>>> "Bob" == Robert A Duff <bobduff@shell01.TheWorld.com> writes:

Bob> Makes sense.  You could write a C program that #include's the
Bob> relevant .h file, and prints out an Ada package spec containing
Bob> the above Ada code.  You could run this as part of your build
Bob> scripts.

Doing it properly is quite painful when you cross-compile, you have to
play dirty unportable tricks such as parsing the assembly file output
by the compiler to extract the value. This is the way I originally did
it in GLADE, it was then used to generate GNAT.Sockets.Constants, but
as you know AdaCore recently switched to parsing the C compiler
output, which was possible because the GCC C compiler (native or
cross) is always available when building the runtime.

Note that you can also do it at run time, even if it costs a few more
cycles to allocate the object, by taking advantage of C ability to
resolve "sizeof" into a static value at compile time. This obliviates
the cross-compilation issues. Here is an example (t.c, t_interface.ads
and test.adb): (note that in real life, one would include the header
defining "struct T" instead of exporting "T_size" in the same file)

/* t.c */
#include <stdlib.h>
struct T {
  char a;
  void *b;
  short c;
  char d;
};
const size_t T_size = sizeof(struct T);

-- t_interface.ads
with Interfaces.C;
package T_Interface is

   T_Size : constant Interfaces.C.size_t;
   pragma Import (C, T_Size, "T_size");

   type Opaque_T is new Interfaces.C.char_array (1 .. T_Size);
   for Opaque_T'Alignment use 8;

private

  pragma Linker_Options ("t.o");

end T_Interface;

-- test.adb
with Ada.Text_IO; use Ada.Text_IO;
with T_Interface; use T_Interface;

procedure Test is
   My_T : Opaque_T;
begin
   Put_Line ("Size of T in bits:" & My_T'Size'Img);
end Test;



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

* Re: Interfacing to C without dynamic memory
  2008-11-15 11:52   ` Samuel Tardieu
@ 2008-11-16 21:31     ` Maciej Sobczak
  0 siblings, 0 replies; 6+ messages in thread
From: Maciej Sobczak @ 2008-11-16 21:31 UTC (permalink / raw)


On 15 Lis, 12:52, Samuel Tardieu <s...@rfc1149.net> wrote:

> Note that you can also do it at run time, even if it costs a few more
> cycles to allocate the object, by taking advantage of C ability to
> resolve "sizeof" into a static value at compile time. This obliviates
> the cross-compilation issues.

> const size_t T_size = sizeof(struct T);

>    T_Size : constant Interfaces.C.size_t;
>    pragma Import (C, T_Size, "T_size");

Neat. This possibility was not obvious to me.

>    type Opaque_T is new Interfaces.C.char_array (1 .. T_Size);
>    for Opaque_T'Alignment use 8;

Of course, I can similarly extract the magic 8 constant from the C
file (sizeof(long long) or such).

Two more questions:

1. Any technical difference between System.Storage_Array and
Interfaces.C.char_array? Considering that the target module is C,
using the latter is more logical, but apart from "name affinity" is
there anything that can make a difference in practice?

2. Now the array has run-time size instead of compile-time size (as
you mentioned yourself). Is there any difference, especially in terms
of memory management? Are such arrays guaranteed to be allocated on
the stack?

--
Maciej Sobczak * www.msobczak.com * www.inspirel.com

Database Access Library for Ada: www.inspirel.com/soci-ada



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

end of thread, other threads:[~2008-11-16 21:31 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-11-14 13:58 Interfacing to C without dynamic memory Maciej Sobczak
2008-11-14 20:35 ` Damien Carbonne
2008-11-15  1:12   ` Randy Brukardt
2008-11-14 23:13 ` Robert A Duff
2008-11-15 11:52   ` Samuel Tardieu
2008-11-16 21:31     ` Maciej Sobczak

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