comp.lang.ada
 help / color / mirror / Atom feed
* How to pass two dimensional arrays to C
@ 2006-07-28 11:06 Jerry
  2006-07-28 12:27 ` Jeffrey Creem
                   ` (7 more replies)
  0 siblings, 8 replies; 23+ messages in thread
From: Jerry @ 2006-07-28 11:06 UTC (permalink / raw)


In my project to write a binding for a popular plotter written in C,
I've hit a brick wall and the problem might be associated with passing
two-dimensionaal arrays to C. I'm using GNAT on OS X and Xcode. I can
pass one-dimensional arrays OK. Here are the pertinent parts from an
example where the problem happens:


      The C


typedef double PLFLT;
typedef int PLINT;

void c_plmesh(PLFLT *x, PLFLT *y, PLFLT **z, PLINT nx, PLINT ny, PLINT
opt);



      The Ada


type Long_Float_Array_1D is array (Integer range <>) of Long_Float;
type Long_Float_Array_2D is array (Integer range <>, integer range <>)
of Long_Float;

subtype PLINT  is Integer;

subtype PL_Float_Array    is Long_Float_Array_1D;
subtype PL_Float_Array_2D is Long_Float_Array_2D;

procedure plmesh(
    x   : PL_Float_Array;
    y   : PL_Float_Array;
    z   : PL_Float_Array_2D;
    nx  : PLINT;
    ny  : PLINT;
    opt : PLINT);
pragma Import(C, plmesh, "c_plmesh");



      And the Ugly


AdaPLplotProject has exited due to signal 11 (SIGSEGV).

This appears during the call to plmesh. Any ideas? 

Thanks,
Jerry




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

* Re: How to pass two dimensional arrays to C
  2006-07-28 11:06 How to pass two dimensional arrays to C Jerry
@ 2006-07-28 12:27 ` Jeffrey Creem
  2006-07-28 12:27 ` Dmitry A. Kazakov
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 23+ messages in thread
From: Jeffrey Creem @ 2006-07-28 12:27 UTC (permalink / raw)


Jerry wrote:
> In my project to write a binding for a popular plotter written in C,
> I've hit a brick wall and the problem might be associated with passing
> two-dimensionaal arrays to C. I'm using GNAT on OS X and Xcode. I can
> pass one-dimensional arrays OK. Here are the pertinent parts from an
> example where the problem happens:
> 
> 
>       The C
> 
> 
> typedef double PLFLT;
> typedef int PLINT;
> 
> void c_plmesh(PLFLT *x, PLFLT *y, PLFLT **z, PLINT nx, PLINT ny, PLINT
> opt);
> 
> 
> 
>       The Ada
> 
> 
> type Long_Float_Array_1D is array (Integer range <>) of Long_Float;
> type Long_Float_Array_2D is array (Integer range <>, integer range <>)
> of Long_Float;
> 
> subtype PLINT  is Integer;
> 
> subtype PL_Float_Array    is Long_Float_Array_1D;
> subtype PL_Float_Array_2D is Long_Float_Array_2D;
> 
> procedure plmesh(
>     x   : PL_Float_Array;
>     y   : PL_Float_Array;
>     z   : PL_Float_Array_2D;
>     nx  : PLINT;
>     ny  : PLINT;
>     opt : PLINT);
> pragma Import(C, plmesh, "c_plmesh");
> 
> 
> 
>       And the Ugly
> 
> 
> AdaPLplotProject has exited due to signal 11 (SIGSEGV).
> 
> This appears during the call to plmesh. Any ideas? 
> 
> Thanks,
> Jerry
> 


Perhaps the C code is not really expecting a two dimensional array but 
rather an array of pointers to arrays.

You could try using some of the in progress/alpha swig work (only in SVN 
at the moment at gnuada.sf.net) to autogenerate some bindings to the 
plplot library since plplot appears to come with a SWIG compatible input 
file.

http://plplot.cvs.sourceforge.net/plplot/plplot/bindings/swig-support/

Even if you decide that the swig stuff is not workable yet, perhaps the 
bindings it creates will be enough to give you a hint as to what the 
real structure is here.



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

* Re: How to pass two dimensional arrays to C
  2006-07-28 11:06 How to pass two dimensional arrays to C Jerry
  2006-07-28 12:27 ` Jeffrey Creem
@ 2006-07-28 12:27 ` Dmitry A. Kazakov
  2006-07-28 16:53 ` Adam Beneschan
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 23+ messages in thread
From: Dmitry A. Kazakov @ 2006-07-28 12:27 UTC (permalink / raw)


On 28 Jul 2006 04:06:59 -0700, Jerry wrote:

> In my project to write a binding for a popular plotter written in C,
> I've hit a brick wall and the problem might be associated with passing
> two-dimensionaal arrays to C.

Well, C does not have arrays (of either dimensionality), so you cannot pass
an Ada (true) array to C, as-is. You can pass a pointer to its first
element. 2D arrays in C are often emulated by using pointers to pointers.
This structure is even more distant from Ada's 2D array.

> I'm using GNAT on OS X and Xcode. I can
> pass one-dimensional arrays OK. Here are the pertinent parts from an
> example where the problem happens:
> 
>       The C
> 
> typedef double PLFLT;
> typedef int PLINT;
> 
> void c_plmesh(PLFLT *x, PLFLT *y, PLFLT **z, PLINT nx, PLINT ny, PLINT
> opt);

[...] 
> This appears during the call to plmesh. Any ideas? 

I'd suggest you to start with the Annex B.3. What you need is probably

with Interfaces.C;
with Interfaces.C.Pointers;

type PLFLT is new Interfaces.C.Double;
type PLINT is new Interfaces.C.Int;

type PLFLT_Array is array (PLINT range <>) of aliased PLFLT;
package PLFLT_Pointers is
   new Interfaces.C.Pointers
       (  Index              => PLINT,
          Element            => PLFLT,
          Element_Array      => PLFLT_Array,
          Default_Terminator => 0.0
       );
subtype PLFLT_Ptr is PLFLT_Pointers.Pointer;

type PLFLT_Ptr_Array is array (PLINT range <>) of aliased PLFLT_Ptr;
package PLFLT_Pointers_To_Pointers is
   new Interfaces.C.Pointers
       (  Index              => PLINT,
          Element            => PLFLT_Ptr,
          Element_Array      => PLFLT_Ptr_Array,
          Default_Terminator => null
       );
subtype PLFLT_Ptr_Ptr is PLFLT_Pointers_To_Pointers.Pointer;

procedure plmesh
          (  x   : PLFLT_Ptr;
             y   : PLFLT_Ptr;
             z   : PLFLT_Ptr_Ptr;
             nx  : PLINT;
             ny  : PLINT;
             opt : PLINT
          );
pragma Import(C, plmesh, "c_plmesh"); 

You might wish to write a wrapper to plmesh to be able to pass PLFLT_Array
and PLFLT_Ptr_Array down to it.

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



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

* Re: How to pass two dimensional arrays to C
  2006-07-28 11:06 How to pass two dimensional arrays to C Jerry
  2006-07-28 12:27 ` Jeffrey Creem
  2006-07-28 12:27 ` Dmitry A. Kazakov
@ 2006-07-28 16:53 ` Adam Beneschan
  2006-07-28 20:15 ` Jeffrey R. Carter
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 23+ messages in thread
From: Adam Beneschan @ 2006-07-28 16:53 UTC (permalink / raw)


Jerry wrote:
> In my project to write a binding for a popular plotter written in C,
> I've hit a brick wall and the problem might be associated with passing
> two-dimensionaal arrays to C. I'm using GNAT on OS X and Xcode. I can
> pass one-dimensional arrays OK. Here are the pertinent parts from an
> example where the problem happens:
>
>
>       The C
>
>
> typedef double PLFLT;
> typedef int PLINT;
>
> void c_plmesh(PLFLT *x, PLFLT *y, PLFLT **z, PLINT nx, PLINT ny, PLINT
> opt);

z is not a two-dimensional array; it's an array of pointers.

Can I guess that you're not all that familiar with C?  * means pointer,
so an object of type PLFLT* is a pointer to a PLFLT.  It's common to
use this idiom to pass a one-dimensional array, because the machine
would pass the address of the array anyway (i.e. the address of the
leftmost element of the array), and you can use pointer arithmetic on
the parameters to get the other elements of the array.  (In fact, array
indexing in C is really just a shorthand for pointer arithmetic.)  But
one dimension is all you can do with this idiom; using two *'s doesn't
give you a two-dimensional array.  PLFLT** is a pointer to a pointer to
a PLFLT, which means that the pointer you pass as a parameter has to
point to a memory location that contains another pointer.  So to use
this routine, you'll need to construct an array of pointers somewhere
(shouldn't be too hard using 'Access, if your 2-D array has aliased
components, but don't take my word for this since I haven't tried it
using that compiler).

Hope this helps,
                              -- Adam




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

* Re: How to pass two dimensional arrays to C
  2006-07-28 11:06 How to pass two dimensional arrays to C Jerry
                   ` (2 preceding siblings ...)
  2006-07-28 16:53 ` Adam Beneschan
@ 2006-07-28 20:15 ` Jeffrey R. Carter
  2006-07-28 20:46 ` Jerry
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 23+ messages in thread
From: Jeffrey R. Carter @ 2006-07-28 20:15 UTC (permalink / raw)


Jerry wrote:
> In my project to write a binding for a popular plotter written in C,
> I've hit a brick wall and the problem might be associated with passing
> two-dimensionaal arrays to C. I'm using GNAT on OS X and Xcode. I can
> pass one-dimensional arrays OK. Here are the pertinent parts from an
> example where the problem happens:

C doesn't have 2D arrays, as you've been told. You might try a 1D array 
of 1D arrays in Ada, but I suspect you're going to have to get very low 
level and ugly and pass a C-compatible 1D array of C-compatible pointers 
to C-compatible 1D arrays. You should hide this away somewhere where it 
will never be found by the rest of the application.

-- 
Jeff Carter
"Don't knock masturbation. It's sex with someone I love."
Annie Hall
45



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

* Re: How to pass two dimensional arrays to C
  2006-07-28 11:06 How to pass two dimensional arrays to C Jerry
                   ` (3 preceding siblings ...)
  2006-07-28 20:15 ` Jeffrey R. Carter
@ 2006-07-28 20:46 ` Jerry
  2006-07-28 21:14   ` Jeffrey Creem
                     ` (2 more replies)
  2006-07-29  5:47 ` REH
                   ` (2 subsequent siblings)
  7 siblings, 3 replies; 23+ messages in thread
From: Jerry @ 2006-07-28 20:46 UTC (permalink / raw)


Thanks for everyone's comments. I have thin bindings to PLplot nearly
complete except for this nagging 2D array thing. (I also have a partial
thick binding plus some easy-to-use one-line plotters as well.)

I understand how C passes arrays and for 1D arrays and I have relied on
GNAT's near-universal use of C calling conventions--I think that this
limits portability to other Ada compilers but "walk before you run"
seems apropos here since I'm learning Ada and bits of C as I go along.
I may later use Interfaces.C if there is sufficient interest.

I have relied on these excerpts from the usual references:

>From the GNAT User's Guide, Calling Conventions: "C. Data will be
passed according to the conventions described in section B.3 of the Ada
95 Reference Manual."

>From RM B.3: "An Ada parameter of an array type with component type T,
of any mode, is passed as a t* argument to a C function, where t is the
C type corresponding to the Ada type T."

>From the GNAT Reference Manual section 10.1: Ada scalar types match C
scalar types. Also, "Ada arrays map directly to C arrays." There is no
mention of 2D arrays specifically.

So I'm thinking that GNAT's calling conventions work for only 1D arrays
and that I'll have to figure out how to convert my 2D z array of
PL_Float_Array_2D into an array of pointers each pointing to a row
(column?) of z. I would want to do this without having to bother the
user with defining his 2D array as a 1D array of 1D arrays.

It seems that I "should" be able to simply pass a pointer (access
variable) to the first location of z, that is, a pointer to
(z'First(1), z'First(2), but I don't see how this could work if C in
fact sees an array of pointers. Wouldn't C's representation of the
array z M x N be, say, M pointers each pointing to the address of the
first element of its M rows? But this would require (M+1)N storage
locations which seems wrong. I would expect that C would simply expect
a blob of MN units of memory where each unit is the sizeof(int) and
rely on M and N being passed as parameters to parse it up correctly.

I've tried to avoid using SWIG as it represents another layer of stuff
that I don't know much about and because it is apparently incomplete
for Ada, but as Jeffrey suggested, even though it is incomplete it
might output some useful results.

I'll look into Dmitry's and Adam's suggestions as well--I don't see how
to make the wrapper for plmesh to minimally affect the user. I want to
keep the Ada facade on the user's array types, and keep the subtype-ing
from Long_Float_Array_2D or similar, whatever is provided by the user
as the base type.

The overall thing I have to figure out, I think, is how to convert a
subtype of Long_Float_Array_2D such as PL_Float_Array_2D into an array
of pointers.

Jerry




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

* Re: How to pass two dimensional arrays to C
  2006-07-28 20:46 ` Jerry
@ 2006-07-28 21:14   ` Jeffrey Creem
  2006-07-28 22:54   ` Björn Persson
  2006-07-29  4:19   ` REH
  2 siblings, 0 replies; 23+ messages in thread
From: Jeffrey Creem @ 2006-07-28 21:14 UTC (permalink / raw)


Jerry wrote:
> Thanks for everyone's comments. I have thin bindings to PLplot nearly
> complete except for this nagging 2D array thing. (I also have a partial
> thick binding plus some easy-to-use one-line plotters as well.)
> 
> I understand how C passes arrays and for 1D arrays and I have relied on
> GNAT's near-universal use of C calling conventions--I think that this
> limits portability to other Ada compilers but "walk before you run"
> seems apropos here since I'm learning Ada and bits of C as I go along.
> I may later use Interfaces.C if there is sufficient interest.
> 
> I have relied on these excerpts from the usual references:
> 
>>From the GNAT User's Guide, Calling Conventions: "C. Data will be
> passed according to the conventions described in section B.3 of the Ada
> 95 Reference Manual."
> 
>>From RM B.3: "An Ada parameter of an array type with component type T,
> of any mode, is passed as a t* argument to a C function, where t is the
> C type corresponding to the Ada type T."
> 
>>From the GNAT Reference Manual section 10.1: Ada scalar types match C
> scalar types. Also, "Ada arrays map directly to C arrays." There is no
> mention of 2D arrays specifically.
> 
> So I'm thinking that GNAT's calling conventions work for only 1D arrays
> and that I'll have to figure out how to convert my 2D z array of
> PL_Float_Array_2D into an array of pointers each pointing to a row
> (column?) of z. I would want to do this without having to bother the
> user with defining his 2D array as a 1D array of 1D arrays.
> 
> It seems that I "should" be able to simply pass a pointer (access
> variable) to the first location of z, that is, a pointer to
> (z'First(1), z'First(2), but I don't see how this could work if C in
> fact sees an array of pointers. Wouldn't C's representation of the
> array z M x N be, say, M pointers each pointing to the address of the
> first element of its M rows? But this would require (M+1)N storage
> locations which seems wrong. I would expect that C would simply expect
> a blob of MN units of memory where each unit is the sizeof(int) and
> rely on M and N being passed as parameters to parse it up correctly.
> 


If C did actually handle real 2d arrays that way, you would be fine but 
you also would not have segfaulted.

This is going to be a pain if you simultaneously want to
1) Keep it a thin binding.
2) Not bother the user with the arrays of pointers to arrays issue
3) Don't want to make the binding horribly inefficient by having to 
create a copy of the data into the C format expected by pl_plot.

I'd probably (in the thin binding) fully expose the ugliness of the 
interface. Not sure you really can avoid it without making a mess..

There are probably a few ways to deal with the thick portion of the 
binding. (e.g. Create a controlled type that hids the array of pointers 
to arrays details and allows the user to resize at will and autocleanup 
storage but keeps the data in something close to what pl_plot expects.

The issue will then be how you let the user access the data. You could 
build cell accessor functions that again hide the details. I've done 
stuff like that but again you do end up with a hit as far as efficiency 
because even with unlining, I am not sure the compiler will be smart 
enough to do strength reduction optimizations on accesses to the 
underlying data in loops. (Could be wrong of course).

Also, the user might want to "see" the data in a more general format 
than the accessors will allow.


I've never really found a solution for this particular style of problem 
that I have been totally happy with. I usually end up with some sort of 
ugly hybrid with a controlled type and a way to access the data in bulk 
as well that requires exposing the details a bit to the user.

It may be that getting worried about efficiency in for this particular 
library might not be worth it but almost any 2d image style library that 
  is designed for heavy use ends up being a resource hog.



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

* Re: How to pass two dimensional arrays to C
  2006-07-28 20:46 ` Jerry
  2006-07-28 21:14   ` Jeffrey Creem
@ 2006-07-28 22:54   ` Björn Persson
  2006-07-29  1:14     ` Jeffrey R. Carter
  2006-07-29  4:19   ` REH
  2 siblings, 1 reply; 23+ messages in thread
From: Björn Persson @ 2006-07-28 22:54 UTC (permalink / raw)


Jerry wrote:
> I would expect that C would simply expect
> a blob of MN units of memory where each unit is the sizeof(int) and
> rely on M and N being passed as parameters to parse it up correctly.

As C, the language, has no concept of two-dimensional arrays, it can't 
expect anything at all about them. c_plmesh, the function, could have 
been written to expect this, but according to the declaration you posted 
it wasn't.

> The overall thing I have to figure out, I think, is how to convert a
> subtype of Long_Float_Array_2D such as PL_Float_Array_2D into an array
> of pointers.

I think something like this should work:

procedure PLmesh
   (x   : PL_Float_Array;
    y   : PL_Float_Array;
    z   : PL_Float_Array_2D;
    opt : PLINT)
is
    Pointer_Array : PLFLT_Ptr_Array(z'Range(1));
begin
    for Index in z'Range(1) loop
       Pointer_Array(Index) := z(Index, 1)'Access;
    end loop;
    c_plmesh(x, y, Pointer_Array, z'Length(1), z'Length(2), opt);
end PLmesh;

That's just to show the idea. Be sure to check the details carefully, 
especially in what order the elements of z are stored.

-- 
Bj�rn Persson                              PGP key A88682FD
                    omb jor ers @sv ge.
                    r o.b n.p son eri nu



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

* Re: How to pass two dimensional arrays to C
  2006-07-28 22:54   ` Björn Persson
@ 2006-07-29  1:14     ` Jeffrey R. Carter
  2006-07-29  7:11       ` Simon Wright
  0 siblings, 1 reply; 23+ messages in thread
From: Jeffrey R. Carter @ 2006-07-29  1:14 UTC (permalink / raw)


Bj�rn Persson wrote:
> 
> I think something like this should work:
> 
> procedure PLmesh
>   (x   : PL_Float_Array;
>    y   : PL_Float_Array;
>    z   : PL_Float_Array_2D;
>    opt : PLINT)
> is
>    Pointer_Array : PLFLT_Ptr_Array(z'Range(1));
> begin
>    for Index in z'Range(1) loop
>       Pointer_Array(Index) := z(Index, 1)'Access;
>    end loop;
>    c_plmesh(x, y, Pointer_Array, z'Length(1), z'Length(2), opt);
> end PLmesh;

No, you need to create a collection of C-compatible
1D arrays, put the rows of Z into them, and store C-compatible
pointers to them in the C-compatible 1D array of pointers. More like

    type Z_Length is new PL_Float_Array (Z_Range (2) );
    pragma Convention (C, Z_Length);

    type Ptr is access Z_Length;
    pragma Convention (C, Ptr);

    type Ptr_List is array (Z'range (1) ) of Ptr;
    pragma Convention (C, Ptr_List);

    A : Ptr_List;
begin
    All_Rows : for Row in Z'range (1) loop
       A (Row) := new Z_Length;

       All_Columns : for Column in Z'range (2) loop
          A (Row) (Column) := Z (Row, Column);
       end loop All_Columns;
    end loop All_Rows;

    C_Plmesh (...);

    -- Reclaim storage? Should be reclaimed automatically since
    -- Ptr goes out of scope
end;

-- 
Jeff Carter
"Don't knock masturbation. It's sex with someone I love."
Annie Hall
45



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

* Re: How to pass two dimensional arrays to C
  2006-07-28 20:46 ` Jerry
  2006-07-28 21:14   ` Jeffrey Creem
  2006-07-28 22:54   ` Björn Persson
@ 2006-07-29  4:19   ` REH
  2006-07-29  4:28     ` REH
  2006-08-14  6:59     ` Dave Thompson
  2 siblings, 2 replies; 23+ messages in thread
From: REH @ 2006-07-29  4:19 UTC (permalink / raw)



"Jerry" <lanceboyle@qwest.net> wrote in message 
news:1154119563.642347.13670@b28g2000cwb.googlegroups.com...
> Thanks for everyone's comments. I have thin bindings to PLplot nearly
> complete except for this nagging 2D array thing. (I also have a partial
> thick binding plus some easy-to-use one-line plotters as well.)
>
With respect to everyone, I believe you being led a little astray.  C does 
have multidimensional arrays.  The problems they are at a lower level than 
Ada's array, and less convenient.  For you to call C, and send it an array 
from Ada, you have to deal with a few issues:
1.  When used as a function parameter, an array will "decay" to a pointer to 
the first element of the array.  This can be inconvenient because 
information about the size and dimensions of the array are lost.  Arrays are 
not "first class" citizens.  You can make them so by wrapping them in 
structs, but this only works if you know the dimensions at compile type. 
So, that is limited use.
2.  What "type" of array?  There can be a couple of different types in C, 
because of their close relationship to pointers.  One type is the simple 
contiguous array:  a[x][y];  Though, one might argue that this is not a 2d 
array, but an array of arrays.  That's just semantics, and doesn't matter. 
The elements will be layed out the same assuming the elements are the same 
and row major ordering is used (so, for Fortran, you would reverse the 
dimensions, since it uses column major ordering).  The second type is called 
a jagged array, which is an array of pointers.  You can simulate this form 
in Ada using an array of access types to the second dimension.  And for both 
languages, an access to and element of the jagged array uses that same 
syntax as a 2d array.
3.  Are the dimensions constant?  As long as all but the first dimension is 
constant, it is simply a matter of giving C a pointer to an object whose 
type is your array type minus the first dimension (again, assume the memory 
is layed out the same).  So, if in Ada we have (ignoring necessay pragmas 
and/or rep. specs.):

type A is array (1 .. 10, 1 .. 10) of Interface.C.int;
type pA is access all A;

we can map this in C several ways.  One easy to understand way is:

typedef A[10][10];

void foo(A* P);

this would map to an Ada procedure thus:

procedure foo(P : in pA);

This has the advantage of C "knowing" the dimensions, but is inconvenient 
because because P has to be dereferenced before dimensions may be applied: 
(*P)[1][2];  *** remember that the dimensions now in C, so the ranges for 
both are 0 to 9.

Another way would be:

typedef A[10];

void foo(A* P);

To map this, we must either use System.Address and lose type safety or 
change A to be an array of array and use an access type to the second 
dimension array (messy).

procedure foo(P : in System.Address);

call foo with A[1,1]'Address.

This makes the C code nicer, in that we can now just say P[1][2];

Both methods assume the dimensions are constant.  If only the first 
dimension is variable, use the second method above, and send C that 
dimension via another parameter.  If both dimensions are variable, this is 
more problematic because C cannot define array types with variable 
dimensions.  All this means is that you have to calculate the indexing 
yourself.  This is a pain and error-prone, but at least it is not 
insurmountable.  For example (assume the element type is still int):

foo(void* P, int x, int y);

Here we calculate P[1][2] thus:

const sx = sizeof(int) * y;
const sy = sizeof(int);
int* I = P;
int v = P + sx * 1 + sy * 2;  *** again, remembering that the dimensions are 
now zero-based.

Messy, but doable.  You should create a function to do the indexing and 
return the element.  If using C99, make it inline.

Jagged arrays are even messier.  These are just arrays of pointers.  They 
allow the creation of arrays where the second dimension is of varying 
length.  The problem is that C will not intrinsically know the lengths, and 
thus you will probably have to give it an array of lengths as a second 
parameter.  But, most likely, if you are going from Ada to C, you aren't 
dealing with these.

I probably made some mistakes above in my haste, but you should get the 
gist.

REH





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

* Re: How to pass two dimensional arrays to C
  2006-07-29  4:19   ` REH
@ 2006-07-29  4:28     ` REH
  2006-07-29  4:30       ` REH
  2006-08-14  6:59     ` Dave Thompson
  1 sibling, 1 reply; 23+ messages in thread
From: REH @ 2006-07-29  4:28 UTC (permalink / raw)



"REH" <me@you.com> wrote in message 
news:6lByg.12373$1Z5.8869@twister.nyroc.rr.com...
> int v = P + sx * 1 + sy * 2;

I told you I'd make a mistake!  Since C will already adjust for the element 
size, the above is wrong.  We either have to cast to a char*, and do the 
math, and to a int*, or more simply:

int v = P + y * 1 + 2;


REH






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

* Re: How to pass two dimensional arrays to C
  2006-07-29  4:28     ` REH
@ 2006-07-29  4:30       ` REH
  0 siblings, 0 replies; 23+ messages in thread
From: REH @ 2006-07-29  4:30 UTC (permalink / raw)



"REH" <me@you.com> wrote in message 
news:ntByg.12374$1Z5.5217@twister.nyroc.rr.com...
>
> int v = P + y * 1 + 2;
>
Agh!!!  Forget to dereference (told you it was messy--all these years of Ada 
has spoiled me):

int v = *(P + y * 1 + 2)
or
int v = P[y * 1 + 2];

OK, it's late and off to bed 





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

* Re: How to pass two dimensional arrays to C
  2006-07-28 11:06 How to pass two dimensional arrays to C Jerry
                   ` (4 preceding siblings ...)
  2006-07-28 20:46 ` Jerry
@ 2006-07-29  5:47 ` REH
  2006-08-02  8:20 ` Jerry
  2006-08-05  1:03 ` Jerry
  7 siblings, 0 replies; 23+ messages in thread
From: REH @ 2006-07-29  5:47 UTC (permalink / raw)



"Jerry" <lanceboyle@qwest.net> wrote in message 
news:1154084819.088112.325730@p79g2000cwp.googlegroups.com...
> In my project to write a binding for a popular plotter written in C,
> I've hit a brick wall and the problem might be associated with passing
> two-dimensionaal arrays to C. I'm using GNAT on OS X and Xcode. I can
> pass one-dimensional arrays OK. Here are the pertinent parts from an
> example where the problem happens:
>
>
>      The C
>
>
> typedef double PLFLT;
> typedef int PLINT;
>
> void c_plmesh(PLFLT *x, PLFLT *y, PLFLT **z, PLINT nx, PLINT ny, PLINT
> opt);
>
>
>
>      The Ada
>
>
> type Long_Float_Array_1D is array (Integer range <>) of Long_Float;
> type Long_Float_Array_2D is array (Integer range <>, integer range <>)
> of Long_Float;
>
> subtype PLINT  is Integer;
>
> subtype PL_Float_Array    is Long_Float_Array_1D;
> subtype PL_Float_Array_2D is Long_Float_Array_2D;
>
> procedure plmesh(
>    x   : PL_Float_Array;
>    y   : PL_Float_Array;
>    z   : PL_Float_Array_2D;
>    nx  : PLINT;
>    ny  : PLINT;
>    opt : PLINT);
> pragma Import(C, plmesh, "c_plmesh");
>
>
>
>      And the Ugly
>
>
> AdaPLplotProject has exited due to signal 11 (SIGSEGV).
>
> This appears during the call to plmesh. Any ideas?
>
> Thanks,
> Jerry
>

That's because C is not expecting to see a 2d array, but an array of 
pointers (jagged array).  If C were looking for an actual 2d array of 
contiguous memory, you would see something like:

void foo(int A[][10]);

or even:

void foo(int (*A)[10]);

What you need to do is give it an array of access types to arrays. 
Something like:

type D2 is array (1 .. 10) of Interface.C.double;
pragma Convention(C, D2);

type PD2 is access all D2;
pragma Convention(C, PD2);

type D2Arrays is array (1 .. 3) of aliased D2;
pragma Convention(C, D2Arrays);

type D1 is array (1 .. 3) of PD2;
pragma Convention(C, D1);

T : D2Arrays;
Z : D1 := (1 => T(1)'Access, 2 => T(2)'Access, 3 => T(3)'Access) ;

Z should now be usable in your function for PLFLT **

Note, that PLFLT ** alone doesn't tell you what C really wants.  I had to go 
on the internet and search for your function to find out.  It could have 
also meant the function was expecting a pointer to a pointer (a pointer by 
reference, so a pointer can be "returned").  It could also have meant that 
the function was going to allocate and "return" a 1d array.  It could also 
have been an actual 2d array (with unknown dimensions).

REH





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

* Re: How to pass two dimensional arrays to C
  2006-07-29  1:14     ` Jeffrey R. Carter
@ 2006-07-29  7:11       ` Simon Wright
  2006-07-29 22:12         ` Jeffrey R. Carter
  0 siblings, 1 reply; 23+ messages in thread
From: Simon Wright @ 2006-07-29  7:11 UTC (permalink / raw)


"Jeffrey R. Carter" <spam.not.jrcarter@acm.not.spam.org> writes:

> No, you need to create a collection of C-compatible 1D arrays, put
> the rows of Z into them, and store C-compatible pointers to them in
> the C-compatible 1D array of pointers. More like

I would have thought that, provided you choose the right orientation,
the rows would be as expected by C (ie contiguous values).

In

   type M is array (Integer range <>, Integer range <>) of Float;
   --               Column            Row

the Row elements would be contiguous? (I can see there _might_ be
compiler differences, and ISTR that GNAT has a way of specifying
Fortran conventions).

(perhaps that should be "aliased Float")



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

* Re: How to pass two dimensional arrays to C
  2006-07-29  7:11       ` Simon Wright
@ 2006-07-29 22:12         ` Jeffrey R. Carter
  2006-07-30 11:18           ` Simon Wright
  2006-07-30 11:20           ` Simon Wright
  0 siblings, 2 replies; 23+ messages in thread
From: Jeffrey R. Carter @ 2006-07-29 22:12 UTC (permalink / raw)


Simon Wright wrote:
> 
> I would have thought that, provided you choose the right orientation,
> the rows would be as expected by C (ie contiguous values).
> 
> In
> 
>    type M is array (Integer range <>, Integer range <>) of Float;
>    --               Column            Row
> 
> the Row elements would be contiguous? (I can see there _might_ be
> compiler differences, and ISTR that GNAT has a way of specifying
> Fortran conventions).

The 1st dimension is considered to index the rows and the 2nd, the 
columns (from the mathematical notation for 2D matrices). In Ada, all 
the components of the 1st row are stored in sequence, followed by all 
the components of the next row, and so on. "An implementation should 
normally represent multidimensional arrays in row-major order, 
consistent with the notation used for multidimensional array aggregates 
(see 4.3.3). However, if a pragma Convention(Fortran, ...) applies to a 
multidimensional array type, then column-major order should be used 
instead (see B.5, ``Interfacing with Fortran'')." (ARM 3.6.2). (Note 
that convention FORTRAN is defined by the language, not GNAT specific.)

In other words, the 2nd, or column, index varies most quickly with 
increasing address.

So to my mind, and using the notation I've described here, the row 
elements are contiguous. So I don't know whether we agree or disagree.

-- 
Jeff Carter
"Whatever it is, I'm against it."
Horse Feathers
46



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

* Re: How to pass two dimensional arrays to C
  2006-07-29 22:12         ` Jeffrey R. Carter
@ 2006-07-30 11:18           ` Simon Wright
  2006-07-30 11:20           ` Simon Wright
  1 sibling, 0 replies; 23+ messages in thread
From: Simon Wright @ 2006-07-30 11:18 UTC (permalink / raw)


"Jeffrey R. Carter" <spam.not.jrcarter@acm.not.spam.org> writes:

> So to my mind, and using the notation I've described here, the row
> elements are contiguous. So I don't know whether we agree or
> disagree.

We agree, I think.

I don't remember the last time I wrote anything to do with matrices,
which is some sort of explanation; if you have a 4x4 state transition
table it's not an issue!

with Ada.Text_IO; use Ada.Text_IO;
with System.Storage_Elements; use System.Storage_Elements;
procedure Rows_And_Columns is
   package Address_IO is new Modular_IO (Integer_Address); use Address_IO;
   type Matrix is array (Integer range <>, Integer range <>) of Integer;
   M : Matrix (0 .. 2, 0 .. 3);
begin
   for A in M'Range (1) loop
      for B in M'Range (2) loop
         Put (To_Integer (M (A, B)'Address));
      end loop;
      New_Line;
   end loop;
end Rows_And_Columns;

grendel:~/tmp simon$ ./rows_and_columns 
 3221223544 3221223548 3221223552 3221223556
 3221223560 3221223564 3221223568 3221223572
 3221223576 3221223580 3221223584 3221223588

so the last index is the one that has contiguous elements. I guess I
have never looked up 'row-major order' before.



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

* Re: How to pass two dimensional arrays to C
  2006-07-29 22:12         ` Jeffrey R. Carter
  2006-07-30 11:18           ` Simon Wright
@ 2006-07-30 11:20           ` Simon Wright
  1 sibling, 0 replies; 23+ messages in thread
From: Simon Wright @ 2006-07-30 11:20 UTC (permalink / raw)


"Jeffrey R. Carter" <spam.not.jrcarter@acm.not.spam.org> writes:

> Simon Wright wrote:
>> I would have thought that, provided you choose the right
>> orientation,
>> the rows would be as expected by C (ie contiguous values).
>> In
>>    type M is array (Integer range <>, Integer range <>) of Float;
>>    --               Column            Row

This was silly of me, clearly these are the wrong way round.

>> the Row elements would be contiguous? (I can see there _might_ be
>> compiler differences, and ISTR that GNAT has a way of specifying
>> Fortran conventions).



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

* Re: How to pass two dimensional arrays to C
  2006-07-28 11:06 How to pass two dimensional arrays to C Jerry
                   ` (5 preceding siblings ...)
  2006-07-29  5:47 ` REH
@ 2006-08-02  8:20 ` Jerry
  2006-08-02  9:03   ` Dmitry A. Kazakov
  2006-08-02 18:25   ` Björn Persson
  2006-08-05  1:03 ` Jerry
  7 siblings, 2 replies; 23+ messages in thread
From: Jerry @ 2006-08-02  8:20 UTC (permalink / raw)


I have attempted to implement the approach suggested by Dmitry and
Björn. With apologies for a posting a fair bit of code, here is an
abbreviated version of what I currently have, with file names (GNAT)
indicated by =====. I hope I haven't edited out something relevant.

=============== my_common.ads
package My_Common is
    type Long_Float_Array_1D is array (Integer range <>) of aliased
Long_Float;
    type Long_Float_Array_2D is array (Integer range <>, integer range
<>) of aliased Long_Float;
end My_Common;



=============== plplotthin.ads
with
    Interfaces.C,
    Interfaces.C.Pointers,
    My_Common;
use
    Interfaces.C,
    Ada.Text_IO;

package PLplotThin is
    subtype PLINT  is Integer;
    subtype PLFLT  is Long_Float;
    subtype PL_Float_Array    is My_Common.Long_Float_Array_1D;
    subtype PL_Float_Array_2D is My_Common.Long_Float_Array_2D;

    package PLFLT_Pointers is new Interfaces.C.Pointers
       (Index              => PLINT,
        Element            => PLFLT,
        Element_Array      => PL_Float_Array, -- was PLFLT_Array
        Default_Terminator => 0.0);
    subtype PLFLT_Ptr is PLFLT_Pointers.Pointer;
    type PLFLT_Ptr_Array is array (PLINT range <>) of aliased
PLFLT_Ptr;

    procedure plmesh(
		x   : PL_Float_Array;
		y   : PL_Float_Array;
		z   : PLFLT_Ptr_Array;
		nx  : PLINT;
		ny  : PLINT;
		opt : PLINT);
    pragma Import(C, plmesh, "c_plmesh");
end PLplotThin;


=============== plplot.adb
with
    PLplotThin,
    My_Common,
    Interfaces.C.Pointers,
    Interfaces.C;
use
    PLplotThin,
    My_Common,
    Interfaces.C;

package body PLplot is

    procedure Mesh_3D
       (x, y    : Long_Float_Array_1D;
        z       : in out Long_Float_Array_2D;
        Options : Integer) is

        Index_Of_First_Column : Integer := z'First(2);

--        package PLFLT_Pointers is new Interfaces.C.Pointers
--           (Index              => PLINT,
--            Element            => PLFLT,
--            Element_Array      => PL_Float_Array, -- was PLFLT_Array
--            Default_Terminator => 0.0);
--
--        subtype PLFLT_Ptr is PLFLT_Pointers.Pointer;
--        type PLFLT_Ptr_Array is array (PLINT range <>) of aliased
PLFLT_Ptr;

        Pointer_Array : PLFLT_Ptr_Array(z'range(1));

    begin
        for Index in z'range(1) loop
            Pointer_Array(Index) := z(Index,
Index_Of_First_Column)'access;
        end loop;
        plmesh(x, y, Pointer_Array, x'Length, y'Length, Options);
    end Mesh_3D;
end PLplot;



This generates one compile-time error, "plplot.adb:990:37: non-local
pointer cannot point to local object." With a rather lucky google hit,
I was led to Cohen's book, 2nd edition, page 356, where he states the
"Acces-Type Lifetime Rule: An attribute X'Access yielding a result
belonging to an access type T is only allowed if X can remain in
existence at least as long as T."

That kinda made sense to me, so I moved the declaration of the package
PLFLT_Pointers is new Interfaces.C.Pointers along with its two
associated subtype and type declarations inside the scope of procedure
Mesh_3D, where you can see it commented-out above. With this
uncommented and the similar declarations in plplotthin.ads
commented-out, I get the compile error "plplotthin.ads:1025:56:
"PLFLT_Ptr_Array" is undefined." Duh.

So how do I fix this conundrum?

Jerry




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

* Re: How to pass two dimensional arrays to C
  2006-08-02  8:20 ` Jerry
@ 2006-08-02  9:03   ` Dmitry A. Kazakov
  2006-08-02 10:22     ` Jerry
  2006-08-02 18:25   ` Björn Persson
  1 sibling, 1 reply; 23+ messages in thread
From: Dmitry A. Kazakov @ 2006-08-02  9:03 UTC (permalink / raw)


On 2 Aug 2006 01:20:34 -0700, Jerry wrote:

> =============== my_common.ads
> package My_Common is
>     type Long_Float_Array_1D is array (Integer range <>) of aliased
> Long_Float;
>     type Long_Float_Array_2D is array (Integer range <>, integer range
> <>) of aliased Long_Float;

You cannot be sure that Integer is Interfaces.C.Int and Long_Float is 
Interfaces.C.Double.

> end My_Common;
> 
> =============== plplotthin.ads
> with
>     Interfaces.C,
>     Interfaces.C.Pointers,
>     My_Common;
> use
>     Interfaces.C,
>     Ada.Text_IO;
> 
> package PLplotThin is
>     subtype PLINT  is Integer;
>     subtype PLFLT  is Long_Float;

Same as above.

>     subtype PL_Float_Array    is My_Common.Long_Float_Array_1D;
>     subtype PL_Float_Array_2D is My_Common.Long_Float_Array_2D;
> 
>     package PLFLT_Pointers is new Interfaces.C.Pointers
>        (Index              => PLINT,
>         Element            => PLFLT,
>         Element_Array      => PL_Float_Array, -- was PLFLT_Array
>         Default_Terminator => 0.0);
>     subtype PLFLT_Ptr is PLFLT_Pointers.Pointer;
>     type PLFLT_Ptr_Array is array (PLINT range <>) of aliased
> PLFLT_Ptr;
> 
>     procedure plmesh(
> 		x   : PL_Float_Array;
> 		y   : PL_Float_Array;
> 		z   : PLFLT_Ptr_Array;
> 		nx  : PLINT;
> 		ny  : PLINT;
> 		opt : PLINT);
>     pragma Import(C, plmesh, "c_plmesh");
> end PLplotThin;
> 
> =============== plplot.adb
> with
>     PLplotThin,
>     My_Common,
>     Interfaces.C.Pointers,
>     Interfaces.C;
> use
>     PLplotThin,
>     My_Common,
>     Interfaces.C;
> 
> package body PLplot is
> 
>     procedure Mesh_3D
>        (x, y    : Long_Float_Array_1D;
>         z       : in out Long_Float_Array_2D;
>         Options : Integer) is
> 
>         Index_Of_First_Column : Integer := z'First(2);
> 
>        Pointer_Array : PLFLT_Ptr_Array(z'range(1));
>
>    begin
>        for Index in z'range(1) loop
>            Pointer_Array(Index) := z(Index, Index_Of_First_Column)'access;

Use Unchecked_Access here.

>       end loop;
>      plmesh(x, y, Pointer_Array, x'Length, y'Length, Options);
>    end Mesh_3D;
> end PLplot;

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



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

* Re: How to pass two dimensional arrays to C
  2006-08-02  9:03   ` Dmitry A. Kazakov
@ 2006-08-02 10:22     ` Jerry
  0 siblings, 0 replies; 23+ messages in thread
From: Jerry @ 2006-08-02 10:22 UTC (permalink / raw)



Dmitry A. Kazakov wrote:
> On 2 Aug 2006 01:20:34 -0700, Jerry wrote:
>
> > =============== my_common.ads
> > package My_Common is
> >     type Long_Float_Array_1D is array (Integer range <>) of aliased
> > Long_Float;
> >     type Long_Float_Array_2D is array (Integer range <>, integer range
> > <>) of aliased Long_Float;
>
> You cannot be sure that Integer is Interfaces.C.Int and Long_Float is
> Interfaces.C.Double.

My understanding is that with GNAT, these equivalences are guaranteed.
I know that what I've done is bad practice for portability, but I don't
think that it is the source of my problems. Please correct me if I am
wrong. However, I'll double-check this.

>
> > end My_Common;
> >
> > =============== plplotthin.ads
> > with
> >     Interfaces.C,
> >     Interfaces.C.Pointers,
> >     My_Common;
> > use
> >     Interfaces.C,
> >     Ada.Text_IO;
> >
> > package PLplotThin is
> >     subtype PLINT  is Integer;
> >     subtype PLFLT  is Long_Float;
>
> Same as above.
>
> >     subtype PL_Float_Array    is My_Common.Long_Float_Array_1D;
> >     subtype PL_Float_Array_2D is My_Common.Long_Float_Array_2D;
> >
> >     package PLFLT_Pointers is new Interfaces.C.Pointers
> >        (Index              => PLINT,
> >         Element            => PLFLT,
> >         Element_Array      => PL_Float_Array, -- was PLFLT_Array
> >         Default_Terminator => 0.0);
> >     subtype PLFLT_Ptr is PLFLT_Pointers.Pointer;
> >     type PLFLT_Ptr_Array is array (PLINT range <>) of aliased
> > PLFLT_Ptr;
> >
> >     procedure plmesh(
> > 		x   : PL_Float_Array;
> > 		y   : PL_Float_Array;
> > 		z   : PLFLT_Ptr_Array;
> > 		nx  : PLINT;
> > 		ny  : PLINT;
> > 		opt : PLINT);
> >     pragma Import(C, plmesh, "c_plmesh");
> > end PLplotThin;
> >
> > =============== plplot.adb
> > with
> >     PLplotThin,
> >     My_Common,
> >     Interfaces.C.Pointers,
> >     Interfaces.C;
> > use
> >     PLplotThin,
> >     My_Common,
> >     Interfaces.C;
> >
> > package body PLplot is
> >
> >     procedure Mesh_3D
> >        (x, y    : Long_Float_Array_1D;
> >         z       : in out Long_Float_Array_2D;
> >         Options : Integer) is
> >
> >         Index_Of_First_Column : Integer := z'First(2);
> >
> >        Pointer_Array : PLFLT_Ptr_Array(z'range(1));
> >
> >    begin
> >        for Index in z'range(1) loop
> >            Pointer_Array(Index) := z(Index, Index_Of_First_Column)'access;
>
> Use Unchecked_Access here.

I tried that before and got a segfault.

>
> >       end loop;
> >      plmesh(x, y, Pointer_Array, x'Length, y'Length, Options);
> >    end Mesh_3D;
> > end PLplot;
>
> --
> Regards,
> Dmitry A. Kazakov
> http://www.dmitry-kazakov.de

Best,
Jerry




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

* Re: How to pass two dimensional arrays to C
  2006-08-02  8:20 ` Jerry
  2006-08-02  9:03   ` Dmitry A. Kazakov
@ 2006-08-02 18:25   ` Björn Persson
  1 sibling, 0 replies; 23+ messages in thread
From: Björn Persson @ 2006-08-02 18:25 UTC (permalink / raw)


Jerry wrote:
> That kinda made sense to me, so I moved the declaration of the package
> PLFLT_Pointers is new Interfaces.C.Pointers along with its two
> associated subtype and type declarations inside the scope of procedure
> Mesh_3D, where you can see it commented-out above. With this
> uncommented and the similar declarations in plplotthin.ads
> commented-out, I get the compile error "plplotthin.ads:1025:56:
> "PLFLT_Ptr_Array" is undefined." Duh.
> 
> So how do I fix this conundrum?

Moving the declaration of plmesh into Mesh_3D isn't an option?

-- 
Bj�rn Persson                              PGP key A88682FD
                    omb jor ers @sv ge.
                    r o.b n.p son eri nu



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

* Re: How to pass two dimensional arrays to C
  2006-07-28 11:06 How to pass two dimensional arrays to C Jerry
                   ` (6 preceding siblings ...)
  2006-08-02  8:20 ` Jerry
@ 2006-08-05  1:03 ` Jerry
  7 siblings, 0 replies; 23+ messages in thread
From: Jerry @ 2006-08-05  1:03 UTC (permalink / raw)


I want to close off this thread for the record.

Many thanks to everyone who helped me.

I have a working solution, pasted in sketch form below.

My only complaint with this solution is that I wanted to keep the thin
bindings, PlplotThin.ads, available and as a complete binding to anyone
who prefers it, if for no other reason than it is notationally closer
to the documentation (and _was_ essentially complete while the thick
binding will be finished only as my spare time permits). With this
solution, any PLplot subprogram which takes a two-dimensional array is
no longer accessible from the thin binding.

Maybe someday I'll learn enough Ada to find a way around this, but for
now, this works:


=============== my_common.ads
package My_Common is
    type Long_Float_Array_1D is array (Integer range <>) of aliased
Long_Float;
    type Long_Float_Array_2D is array (Integer range <>, integer range
<>) of aliased Long_Float;
end My_Common;



=============== plplotthin.ads
with
    My_Common;

package PLplotThin is
    subtype PLINT  is Integer;
    subtype PLFLT  is Long_Float;
    subtype PL_Float_Array    is My_Common.Long_Float_Array_1D;
    subtype PL_Float_Array_2D is My_Common.Long_Float_Array_2D;

    -- procedure plmesh is not defined in this package, but
	-- but in the thick binding PLplot wrapped in Mesh_3D.
	-- The same applies for the various other 3D plotters such
	-- as plmeshc, plot3d, plot3dc, and others which take a
	-- two-dimensional array as a parameter.
	-- These routines are unavailable from this thin binding.
end PLplotThin;


=============== plplot.adb
with
    PLplotThin,
    My_Common,
    Interfaces.C.Pointers,
    Interfaces.C;
use
    PLplotThin,
    My_Common,
    Interfaces.C;

package body PLplot is

    procedure Mesh_3D
       (x, y    : Long_Float_Array_1D; -- surface definition points
        z       : in out Long_Float_Array_2D; -- height of surface at
definition points
        Options : Integer) is

        package PL_Float_Pointers_Local is new Interfaces.C.Pointers
           (Index              => Integer,
            Element            => Long_Float,
            Element_Array      => Long_Float_Array_1D,
            Default_Terminator => 0.0);
        use type PL_Float_Pointers_Local.Pointer; -- as in RM B.3.2
        type PL_Float_Pointer_Array_Local is array (Integer range <>)
of PL_Float_Pointers_Local.Pointer; -- array of pointers to Long_Floats
which represent the first element of each row of z in C-land

        Index_Of_First_Column : Integer := z'First(2);
        z_As_Pointers : PL_Float_Pointer_Array_Local (z'range(1));

        procedure
        plmesh_local(x : PL_Float_Array; y : PL_Float_Array; z :
PL_Float_Pointer_Array_Local; nx : Integer; ny : Integer; opt :
Integer);
        pragma Import(C, plmesh_local, "c_plmesh");

    begin
        for Index in z'range(1) loop
            z_As_Pointers(Index) := z(Index,
Index_Of_First_Column)'access;
        end loop;
        plmesh_local(x, y, z_As_Pointers, x'Length, y'Length, Options);
-- pass z_As_Pointers here rather than z
    end Mesh_3D;

end PLplot;


=============== Testing_Thick_Binding.adb
with
    PlplotThin,
    PLplot,
    My_Common;
use
    PlplotThin,
    PLplot,
    My_Common;

package body Testing_Thick_Binding is
    procedure Main_Testing_Thick_Binding is
        z : aliased Long_Float_Array_2D (1..xpts, 1..ypts);
        pragma Convention(C, z); -- for compatibility with non-GNAT
        x : Long_Float_Array_1D (1..xpts);
        pragma Convention(C, x); -- for compatibility with non-GNAT
        y : Long_Float_Array_1D (1..ypts);
        pragma Convention(C, y); -- for compatibility with non-GNAT
    begin -- Main_Testing_Thick_Binding
		-- ...
		-- Calculate x, y, z.
		-- etc.
		-- ...
       	Mesh_3D(x, y, z, Options); -- call plmesh wrapper
		-- Clean up.
	end Main_Testing_Thick_Binding;
end Testing_Thick_Binding;




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

* Re: How to pass two dimensional arrays to C
  2006-07-29  4:19   ` REH
  2006-07-29  4:28     ` REH
@ 2006-08-14  6:59     ` Dave Thompson
  1 sibling, 0 replies; 23+ messages in thread
From: Dave Thompson @ 2006-08-14  6:59 UTC (permalink / raw)


On Sat, 29 Jul 2006 04:19:14 GMT, "REH" <me@you.com> wrote:

> 
> "Jerry" <lanceboyle@qwest.net> wrote in message 
> news:1154119563.642347.13670@b28g2000cwb.googlegroups.com...
> > Thanks for everyone's comments. I have thin bindings to PLplot nearly
> > complete except for this nagging 2D array thing. (I also have a partial
> > thick binding plus some easy-to-use one-line plotters as well.)
> >
> With respect to everyone, I believe you being led a little astray.  C does 
> have multidimensional arrays.  The problems they are at a lower level than 

Basically agree.

> Ada's array, and less convenient.  For you to call C, and send it an array 
> from Ada, you have to deal with a few issues:
> 1.  When used as a function parameter, an array will "decay" to a pointer to 
> the first element of the array.  This can be inconvenient because 
> information about the size and dimensions of the array are lost.  Arrays are 
> not "first class" citizens.  You can make them so by wrapping them in 
> structs, but this only works if you know the dimensions at compile type. 
> So, that is limited use.

Exactly.

> 2.  What "type" of array?  There can be a couple of different types in C, 
> because of their close relationship to pointers.  One type is the simple 
> contiguous array:  a[x][y];  Though, one might argue that this is not a 2d 
> array, but an array of arrays.  That's just semantics, and doesn't matter. 
> The elements will be layed out the same assuming the elements are the same 
> and row major ordering is used (so, for Fortran, you would reverse the 
> dimensions, since it uses column major ordering).  The second type is called 
> a jagged array, which is an array of pointers.  You can simulate this form 
> in Ada using an array of access types to the second dimension.  And for both 
> languages, an access to and element of the jagged array uses that same 
> syntax as a 2d array.

Almost. In C ary-of-ary and ary-of-ptr(-for-ary) are both X [I] [J].
In Ada array-of-array is X (I) (J) and so is array-of-access-to-array
since the dereference is implied; but true 2D array is X (I, J).

> 3.  Are the dimensions constant?  As long as all but the first dimension is 
> constant, it is simply a matter of giving C a pointer to an object whose 
> type is your array type minus the first dimension (again, assume the memory 

Canonically, right.

> is layed out the same).  So, if in Ada we have (ignoring necessay pragmas 
> and/or rep. specs.):
> 
> type A is array (1 .. 10, 1 .. 10) of Interface.C.int;
> type pA is access all A;
> 
> we can map this in C several ways.  One easy to understand way is:
> 
But to be clear, NOT the way you just said (drop the first dimension).

> typedef A[10][10];
> 
> void foo(A* P);
> 
> this would map to an Ada procedure thus:
> 
> procedure foo(P : in pA);
> 
> This has the advantage of C "knowing" the dimensions, but is inconvenient 
> because because P has to be dereferenced before dimensions may be applied: 
> (*P)[1][2];  *** remember that the dimensions now in C, so the ranges for 
> both are 0 to 9.
> 
> Another way would be:
> 
> typedef A[10];
> 
> void foo(A* P);
> 
> To map this, we must either use System.Address and lose type safety or 
> change A to be an array of array and use an access type to the second 
> dimension array (messy).
> 
> procedure foo(P : in System.Address);
> 
> call foo with A[1,1]'Address.
> 
Surely you mean someA (1,1) 'Address . 

> This makes the C code nicer, in that we can now just say P[1][2];
> 
Aside: You don't need the typedef's in C. They _don't_ create really
new types, only aliases/abbreviations. (But I'm fine with using them
for the pedagogical example, as they allow you to discuss different
points (!) separately.)

> Both methods assume the dimensions are constant.  If only the first 
> dimension is variable, use the second method above, and send C that 
> dimension via another parameter.  If both dimensions are variable, this is 
> more problematic because C cannot define array types with variable 
> dimensions.  All this means is that you have to calculate the indexing 

As of C99 it can standardly, and although complete C99 implementations
are not common, some features including this one (called Variable
Length Arrays for the actual objects, and Variably Modified types for
some of the other types related to them like pointers) are becoming
common, and indeed were in GCC even before C99.

> yourself.  This is a pain and error-prone, but at least it is not 
> insurmountable.  For example (assume the element type is still int):
> 
Of course, doing this way (by hand) is still perfectly legal also.

> foo(void* P, int x, int y);
> 
Probably better unsigned /*int*/, or even better size_t. 
Dimensions cannot be negative, and cannot validly be zero; C doesn't
have Positive, but it does (almost) have Natural.

> Here we calculate P[1][2] thus:
> 
> const sx = sizeof(int) * y;
> const sy = sizeof(int);

const int (or better size_t) in C99; implicit int is (finally!) gone,
and types are NOT automatic like they are in Ada renames --
although a fairly similar feature is being considered for _C++_.

But per your correction the sizeof(int) factors aren't needed anyway.

> int* I = P;
> int v = P + sx * 1 + sy * 2;  *** again, remembering that the dimensions are 
> now zero-based.
> 

There's no need to pass the base pointer as void* and then convert it
to whatever*. Unless you are trying to make a type-generic utility
routine(s), and you can't really do that right in C, so barring
exigent circumstances I wouldn't confuse matters by trying.

> Messy, but doable.  You should create a function to do the indexing and 
> return the element.  If using C99, make it inline.
> 
I don't think it's worth making it a function. But then I've needed to
do manual-Ndim in enough languages (and systems) back to early FORTRAN
and BASIC that it's almost subconscious.

But I agree make it inline if you do. (Again actually supported in a
lot of compilers that don't support all of C99. But not all. But then
you can easily enough #define it away it necessary)

> Jagged arrays are even messier.  These are just arrays of pointers.  They 
> allow the creation of arrays where the second dimension is of varying 
> length.  The problem is that C will not intrinsically know the lengths, and 
> thus you will probably have to give it an array of lengths as a second 
> parameter.  But, most likely, if you are going from Ada to C, you aren't 
> dealing with these.
> 

- David.Thompson1 at worldnet.att.net



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

end of thread, other threads:[~2006-08-14  6:59 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-07-28 11:06 How to pass two dimensional arrays to C Jerry
2006-07-28 12:27 ` Jeffrey Creem
2006-07-28 12:27 ` Dmitry A. Kazakov
2006-07-28 16:53 ` Adam Beneschan
2006-07-28 20:15 ` Jeffrey R. Carter
2006-07-28 20:46 ` Jerry
2006-07-28 21:14   ` Jeffrey Creem
2006-07-28 22:54   ` Björn Persson
2006-07-29  1:14     ` Jeffrey R. Carter
2006-07-29  7:11       ` Simon Wright
2006-07-29 22:12         ` Jeffrey R. Carter
2006-07-30 11:18           ` Simon Wright
2006-07-30 11:20           ` Simon Wright
2006-07-29  4:19   ` REH
2006-07-29  4:28     ` REH
2006-07-29  4:30       ` REH
2006-08-14  6:59     ` Dave Thompson
2006-07-29  5:47 ` REH
2006-08-02  8:20 ` Jerry
2006-08-02  9:03   ` Dmitry A. Kazakov
2006-08-02 10:22     ` Jerry
2006-08-02 18:25   ` Björn Persson
2006-08-05  1:03 ` Jerry

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