Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I pass an array of C.double's to a Cgo function?

Tags:

go

cgo

I'm just getting started with CGo and I'm trying to send data to a C library that performs statistical computations on arrays of floats/doubles. What I'm trying to figure out right now is how to send an array of floats, or C.double's, to a CGo function that has a signature like this:

double pop_mean(int numPoints, double a[])

I've figured out how to get in the C.int in there, but I'm having trouble figuring out how to send in an array of doubles.

I haven't yet seen any blog posts or SO Questions about this exact thing, so I thought I'd ask.

The following is my best effort so far.

// Get a basic function to work, while passing in an ARRAY  arr := make([]C.double, 0)
arr = append(arr, C.double(10.0))
arr = append(arr, C.double(20.0))
arr = append(arr, C.double(30.0))
var fixedArray [3]C.double = arr[:]

// ptr := C.CBytes(arr)
// defer C.free(unsafe.Pointer(ptr))

coolMean := C.pop_mean(3, &fixedArray)
fmt.Println("pop_mean (10, 20, 30): ", coolMean)

And this is the error I'm getting:

./main.go:64:6: cannot use arr[:] (type []_Ctype_double) as type [3]_Ctype_double in assignment
./main.go:69:35: cannot use &fixedArray (type *[3]_Ctype_double) as type *_Ctype_double in argument to _Cfunc_pop_mean

How should I be passing an array of C.double to the code?

like image 896
Jonathan Steele Avatar asked Nov 30 '18 00:11

Jonathan Steele


People also ask

How do you pass an entire array to a function?

To pass an entire array to a function, only the name of the array is passed as an argument. result = calculateSum(num); However, notice the use of [] in the function definition. This informs the compiler that you are passing a one-dimensional array to the function.

How an array is passed to a function in C language?

How Arrays are Passed to Functions in C/C++? A whole array cannot be passed as an argument to a function in C++. You can, however, pass a pointer to an array without an index by specifying the array's name. In C, when we pass an array to a function say fun(), it is always treated as a pointer by fun().

How do you pass an array into an address?

Pass By Address with arrays:When we pass the array in by its name, we are passing the address of the first array element. So, the expected parameter is a pointer. Example: // This function receives two integer pointers, which can be names of integer arrays.

Does C pass array by reference?

Passing arrays to functions in C/C++ are passed by reference. Even though we do not create a reference variable, the compiler passes the pointer to the array, making the original array available for the called function's use. Thus, if the function modifies the array, it will be reflected back to the original array.


2 Answers

When an array name is passed to a function, what is passed is the location of the initial element. Within the called function, this argument is a local variable, and so an array name parameter is a pointer, that is, a variable containing an address.

C Programming Language, 2nd Edition


Slice types

A slice is a descriptor for a contiguous segment of an underlying array and provides access to a numbered sequence of elements from that array.

Like arrays, slices are indexable and have a length. The length of a slice s can be discovered by the built-in function len; unlike with arrays it may change during execution. The elements can be addressed by integer indices 0 through len(s)-1. The slice index of a given element may be less than the index of the same element in the underlying array.

A slice, once initialized, is always associated with an underlying array that holds its elements.

The Go Programming Language Specification


Reference: Go Command cgo


For a slice a, the arguments to the pop_mean(int numPoints, double a[]) C function are len(a), the length of the slice underlying array, and &a[0], the address of the first element of the slice underlying array.


In Go, we often hide details in a function. For example, a popMean function,

package main

import (
    "fmt"
)

/*
double pop_mean(int numPoints, double a[]) {
    if (a == NULL || numPoints == 0) {
        return 0;
    }
    double mean = 0;
    for (int i = 0; i < numPoints; i++) {
        mean+=a[i];
    }
    return mean / numPoints;
}
*/
import "C"

func popMean(a []float64) float64 {
    // This is the general case, which includes the special cases
    // of zero-value (a == nil and len(a) == 0)
    // and zero-length (len(a) == 0) slices.
    if len(a) == 0 {
        return 0
    }
    return float64(C.pop_mean(C.int(len(a)), (*C.double)(&a[0])))
}

func main() {
    a := make([]float64, 10)
    for i := range a {
        a[i] = float64(i + 1)
    }

    // slice
    fmt.Println(len(a), a)
    pm := popMean(a)
    fmt.Println(pm)

    // subslice
    b := a[1:4]
    fmt.Println(len(b), b)
    pm = popMean(b)
    fmt.Println(pm)

    // zero length
    c := a[:0]
    fmt.Println(len(c), c)
    pm = popMean(c)
    fmt.Println(pm)

    // zero value (nil)
    var z []float64
    fmt.Println(len(z), z, z == nil)
    pm = popMean(z)
    fmt.Println(pm)
}

Output:

10 [1 2 3 4 5 6 7 8 9 10]
5.5
3 [2 3 4]
3
0 []
0
0 [] true
0
like image 64
peterSO Avatar answered Sep 28 '22 00:09

peterSO


I figured out that you have to send a pointer to the first value in the array, rather than sending a pointer to the first element of the slice, or to the slice itself.

AND I also ran into the problem where I had created a new variable that was assigned the value of the first item in the slice and later created a pointer to that variable (which was no longer a part of the original array), instead of creating a pointer to the first item in the array (like I wanted).

Below is the working code, with comments to help avoid the problem in the paragraph above.

// Get a basic function to work, while passing in an ARRAY

// Create a dummy array of (10,20,30), the mean of which is 20.
arr := make([]C.double, 0)
arr = append(arr, C.double(10.0))
arr = append(arr, C.double(20.0))
arr = append(arr, C.double(30.0))
firstValue := &(arr[0]) // this notation seems to be pretty important... Re-use this!
// if you don't make it a pointer right away, then you make a whole new object in a different location, so the contiguous-ness of the array is jeopardized.
// Because we have IMMEDIATELY made a pointer to the original value,the first value in the array, we have preserved the contiguous-ness of the array.
fmt.Println("array length: ", len(arr))

var arrayLength C.int
arrayLength = C.int(len(arr))
// arrayLength = C.int(2)

fmt.Println("array length we are using: ", arrayLength)

arrayMean := C.pop_mean(arrayLength, firstValue)
fmt.Println("pop_mean (10, 20, 30): ", arrayMean)

This produces the following result:

array length:  3
array length we are using:  3
pop_mean (10, 20, 30):  20

Or if we uncomment the line that changes the arrayLength to be 2, we get this result:

array length:  3
array length we are using:  2
pop_mean (10, 20, 30):  15
like image 21
Jonathan Steele Avatar answered Sep 28 '22 00:09

Jonathan Steele