Как передать массив C.double в функцию Cgo? - PullRequest
0 голосов
/ 30 ноября 2018

Я только начинаю работать с CGo и пытаюсь отправить данные в библиотеку C, которая выполняет статистические вычисления для массивов с плавающей запятой / типа double.Сейчас я пытаюсь выяснить, как отправить массив чисел с плавающей точкой или C.double в функцию CGo с такой сигнатурой:

double pop_mean(int numPoints, double a[])

Я выяснил, какпопасть в C.int там, но у меня возникли проблемы с выяснением, как отправить массив двойных чисел.

Я еще не видел ни одного поста в блоге или ТАКИХ Вопросов об этой конкретной вещи, поэтому я подумал, что задам вопрос.

// 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)

И вот эта ошибка, которую я получаю:

./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

Как мне передать массив C.double в код?

Ответы [ 2 ]

0 голосов
/ 30 ноября 2018

Когда имя массива передается функции, передается местоположение исходного элемента.Внутри вызываемой функции этот аргумент является локальной переменной, и поэтому параметр имени массива является указателем, то есть переменной, содержащей адрес.

C Язык программирования, 2-е издание


Типы срезов

Срез - это дескриптор для непрерывного сегмента базового массива и обеспечивает доступ к пронумерованной последовательности элементовиз этого массива.

Как и массивы, срезы индексируются и имеют длину.Длина среза s может быть обнаружена встроенной функцией len;в отличие от массивов, он может измениться во время выполнения.Элементы могут быть адресованы целочисленными индексами от 0 до len (s) -1.Индекс слайса данного элемента может быть меньше, чем индекс того же элемента в базовом массиве.

После инициализации слайс всегда связан с базовым массивом, который содержит его элементы.

Спецификация языка программирования Go


Ссылка: Команда Go cgo


Для среза a аргументы функции pop_mean(int numPoints, double a[]) C: len(a), длина основного массива среза и &a[0], адрес первого элемента основного массива среза.


В Go мы часто скрываем детали в функции.Например, функция popMean,

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)
}

Вывод:

10 [1 2 3 4 5 6 7 8 9 10]
5.5
3 [2 3 4]
3
0 []
0
0 [] true
0
0 голосов
/ 30 ноября 2018

Я понял, что вам нужно отправлять указатель на первое значение в массиве , а не отправлять указатель на первый элемент слайса или на сам слайс.

И я также столкнулся с проблемой, когда я создал новую переменную, которой было присвоено значение первого элемента в срезе, а затем создал указатель на эту переменную (которая больше не была частью оригинальноймассив), вместо создания указателя на первый элемент в массиве (как я хотел).

Ниже приведен рабочий код с комментариями, которые помогут избежать проблемы в абзаце выше.

// 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)

Это приводит к следующему результату:

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

Или, если мы раскомментируем строку, которая изменяет arrayLength на 2, мы получим этот результат:

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