Доступ к массиву C в golang - PullRequest
0 голосов
/ 10 ноября 2018

У меня есть два файла, module.go и test.py. Моя цель состоит в том, чтобы ускорить некоторые вычисления, которые выполняются в python, но есть проблема с доступом к массиву целых чисел в go.

module.go

package main

import "C"

//export Example
func Example(testArray []C.int) C.int {
    return testArray[2]
}

func main() {}

и простой тестовый файл на python:

from ctypes import *

# Load compiled go module
lib = cdll.LoadLibrary("./gomodule.so")
# We are passing an array of 256 elements and recieving integer
lib.Example.argtypes = [c_int * 256]
lib.Example.restype = c_int
pyarr = [x for x in range(256)]
# Make C array from py array
arr = (c_int * len(pyarr))(*pyarr)
print lib.Example(arr)

После компиляции модуля go с go build -buildmode=c-shared -o gomodule.so module.go и запуска файла python я получил:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x12 pc=0x7fb18b6e688c]

goroutine 17 [running, locked to thread]:
main.Example(...)
        /home/metro/go/src/github.com/golubaca/carinago/module.go:7
main._cgoexpwrap_53c1c00d0ad3_Example(0xa, 0x7fff33a2eac0, 0x7fff33a2ea70, 0x722a921de6cae100)
        _cgo_gotypes.go:47 +0x1c
Aborted (core dumped)

Я понимаю, что массив C отличается от Go, но не могу найти учебник, как получить доступ к его значениям без паники.

Ответы [ 2 ]

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

Это идиоматическое, эффективное решение Go (избегайте размышлений).

module.go * * 1004

package main

import "C"

import "unsafe"

//export Example
func Example(cArray *C.int, cSize C.int, i C.int) C.int {
    gSlice := (*[1 << 30]C.int)(unsafe.Pointer(cArray))[:cSize:cSize]
    return gSlice[i]
}

func main() {}

test.py

from ctypes import *

# Load compiled go module
lib = cdll.LoadLibrary("./gomodule.so")
# We are passing an array of 256 elements and receiving an integer
lib.Example.argtypes = [c_int * 256]
lib.Example.restype = c_int
pyarr = [x for x in range(256)]
# Make C array from py array
arr = (c_int * len(pyarr))(*pyarr)
print lib.Example(arr, len(arr), 4)

Выход:

$ go build -buildmode=c-shared -o gomodule.so module.go
$ python test.py
4
$ 
0 голосов
/ 10 ноября 2018

Массив в C не может быть автоматически приведен к фрагменту в Go. Обратите внимание, что в Go срез состоит из двух частей: длины и указателя на данные основы.

Так что вам придется вручную создать срез из указателя.

package main

import (
    "C"
    "reflect"
    "unsafe"
)

//export Example
func Example(carr *C.int, size int, idx int) C.int {

    // Build the slice manually using unsafe
    var slice []C.int
    header := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
    header.Cap = size
    header.Len = size
    header.Data = uintptr(unsafe.Pointer(carr))

    return slice[idx]
}

func main() {}

Тогда вы бы вызвали экспортированную функцию Go в вашем коде Python, например:

from ctypes import *

# Load compiled go module
lib = cdll.LoadLibrary("./gomodule.so")
# We are passing an array of 256 elements and recieving integer
lib.Example.argtypes = [c_int * 256]
lib.Example.restype = c_int
pyarr = [x for x in range(256)]
# Make C array from py array
arr = (c_int * len(pyarr))(*pyarr)
print lib.Example(arr, len(arr), 4)

В приведенном выше примере должен быть напечатан 4-й индексный элемент массива

...