Calling a c-function that returns a 2-dimensional array

question

#1

Hello,

new to julia. I want to call a c-function like

void foo(double a[], double b[], double c[][n], int n){
    for (int i=0; i<n; i++){
        for (int j=0; j<n; j++){
            c[i][j]=a[i]*b[j];
        }   
    }
 }

from julia.

In julia I have defined the input arrays and will declare the output array, that has two dimensions from the same size. For examble:

n = 5
a = ones(n)
b = fill(2,n)
c = Array{Float64}(n,n)

What are the correct parameters for ccall to fill the output array by the c-function?

Thank’s

peter


#2

Ptr{Float64} <20 char limits…>


#3

This tutorial may be helpful?


#4

I think the following should work:

assert(n == size(c,1) == size(c,2))
ccall((:foo, libname), Void, 
    (Ptr{Cdouble}, Ptr{Cdouble}, Ptr{Ptr{Cdouble}}, Cint), 
    a, b, [view(c,:,j) for j = 1:n], n)

Note that Julia uses column-major ordering, so you may need to transpose the result.

UPDATE: this is incorrect: see comments by @yuyichao and @jameson below


#5

No it’s just a Ptr{Cdouble}.


#6

I assume the OP knows how to pass simple types and how to map them to julia types and only have questions about passing a multidimensional array.

Passing a multidimensional array (or multidimensional array) in C is surprisingly underdocumented AFAICT. I can’t find any clear mention on cppreference (though there’re hints). The best I can find apart from many SO posts that covers parts of the problem each is https://www.eskimo.com/~scs/cclass/int/sx9a.html .

It might worth mentioning this in our doc. It’s worth noting that int a[2][2] and int a** are completely different types in C even though both of them are dereferenced with a[x][y]. They have very different type layout and cannot be converted to each other.


#7

Passing a multidimensional array (or multidimensional array) in C is surprisingly underdocumented AFAICT

Declaring a C argument type as being [], I believe, is a compiler extension (although probably part of the spec now), and isn’t actually part of the C ABI. Most parsers rewrite any number of [] to * in the argument list following the rules for array-pointer decomposition. This isn’t strictly valid, but the direct interpretation of this syntax (passing an array by-value) isn’t defined in the C standard.


#8

Passing array argument is actually mentioned on cppreference http://en.cppreference.com/w/c/language/array and I just noticed that passing in multi dimensional array is actually also mentioned in there to, which is supported since c99.


Transfer Float64 matrix to double **arg in C
#9

Yes, only passing multi-dimensional arrays I find confusing.

Thanks, it’s really a Ptr{Cdouble}. The array have to be transposed before and after the call, as simonbyrne sayed.

For example:

C:

void foo(double c[][4], int n)
{
    int i,j;
    printf("called with\n");
    for(i=0; i<n; i++) {
	for(j=0; j<n; j++) {
            printf("%f ",c[i][j]);
        }
        printf("\n");
    }
    for(i=0; i<n; i++) {
	for(j=0; j<n; j++) {
            c[i][j]=c[i][j]*10;
        }
    }

    printf("returnd with \n"); 
    for(i=0; i<n; i++) {
		for(j=0; j<n; j++) {
            printf("%f ",c[i][j]);
        }
        printf("\n");
    } 
}

julia:

julia> n=4
4

julia> c=[1 2 3 4; 5 6 7 8; 9 10 11 12; 13 14 15 16.]
4×4 Array{Float64,2}:
  1.0   2.0   3.0   4.0
  5.0   6.0   7.0   8.0
  9.0  10.0  11.0  12.0
 13.0  14.0  15.0  16.0

julia> ct=transpose(c)
4×4 Array{Float64,2}:
 1.0  5.0   9.0  13.0
 2.0  6.0  10.0  14.0
 3.0  7.0  11.0  15.0
 4.0  8.0  12.0  16.0

julia> ccall((:foo,"foolib.so"), Void,
           (Ptr{Cdouble}, Cint), ct, n)
called with
1.000000 2.000000 3.000000 4.000000
5.000000 6.000000 7.000000 8.000000
9.000000 10.000000 11.000000 12.000000
13.000000 14.000000 15.000000 16.000000
returns with
10.000000 20.000000 30.000000 40.000000
50.000000 60.000000 70.000000 80.000000
90.000000 100.000000 110.000000 120.000000
130.000000 140.000000 150.000000 160.000000

julia> ct
4×4 Array{Float64,2}:
 10.0  50.0   90.0  130.0
 20.0  60.0  100.0  140.0
 30.0  70.0  110.0  150.0
 40.0  80.0  120.0  160.0

julia> c=transpose(ct)
4×4 Array{Float64,2}:
  10.0   20.0   30.0   40.0
  50.0   60.0   70.0   80.0
  90.0  100.0  110.0  120.0
 130.0  140.0  150.0  160.0

peter