Конвертация из []float32 в **float32
Пытаюсь перенести библиотеку из с++ в go, сгенерировал обертку swig и появилась проблема.
Читаю файл модели как массив []float32 далее его нужно передать в функцию ожидающую **float, и вот какой правильный тип данных создать и как его передать я не знаю, пробовал []*float32{&model[0]}, но не подходит так как тип []*float32.
В с++ был код:
auto model = ReadFile<float>(_MODEL_FILENAME); // model - std::vector<float> model
_models = {model.data()}; // model - std::array<const float *, 4UL>
_model_floats = {static_cast<std::uint32_t>(model.size())}; // model_floats - std::array<uint32_t, 4UL>
function(_handle, _TYPE, 0, 0, _models.data(),_model_floats.data()
Вот код go:
model, err_f := readFile(absPath) // model - []float32
models := ?
model_floats := uint(len(model))
function(conf.Handle, conf.Type, 0, 0, !models!, &model_floats) // Здесь models нужна конвертация в **float
Ответы (1 шт):
Нужно ручками собрать массив указателей.
Вот пример:
package main
/*
#include <stdio.h>
#include <stdint.h>
void myfunc(float** data, uint64_t * sizes, uint64_t num_rows) {
printf("Number of rows: %ld\n", num_rows);
for (uint64_t i = 0 ; i < num_rows; i++) {
for (uint64_t j = 0; j < sizes[i]; j++) {
printf("%.6f ", data[i][j]);
}
putc('\n', stdout);
}
}
*/
import "C"
import (
"runtime"
"unsafe"
)
func main() {
data := [][]C.float{
{
1.0, 2.0, 3.0, 4.0,
},
{
1.1, 2.1, 3.1, 4.1,
},
{
1.2, 2.2, 3.2, 4.2,
},
{
1.3, 2.3, 3.3, 4.3,
},
}
data_ptr := make([]*C.float, len(data))
sizes := make([]C.uint64_t, len(data))
// Защищает указатели от сборщика мусора
pinner := new(runtime.Pinner)
for i, arr := range data {
sizes[i] = C.uint64_t(len(arr))
// Указатель на данные слайса
ptr := unsafe.SliceData(data[i])
// Сообщает сборщику мусора, что указатель ptr используется вне Go
pinner.Pin(ptr)
data_ptr[i] = ptr
}
C.myfunc(
unsafe.SliceData(data_ptr), // аргументы С функций закрепляются автоматически
unsafe.SliceData(sizes),
C.uint64_t(len(sizes)),
)
// вернуть указатели под управление сборщиком мусора
pinner.Unpin()
}
В этом примере функция myfunc принимает массив указателей на float, массив длин и общее число элементов в каждом массиве.
Для передачи данных в Си нужно извлечь указатели на данные каждого массива и сохранить их в массив указателей:
data_ptr := make([]*C.float, len(data))
for i, arr := range data {
// Указатель на данные слайса
ptr := unsafe.SliceData(data[i])
// Сообщает сборщику мусора, что указатель ptr используется вне Go
data_ptr[i] = ptr
}
Слайс data_ptr превращается в указатель **float вызовом встроенной функции unsafe.SliceData.
Тут необходим один трюк. Так как в Go сборщик мусора работает параллельно с остальным кодом, может такое случиться, что данные по указателю ptr окажутся прибранными сборщиком мусора. Поэтому указатель ptr нужно "прибить гвоздями" в памяти. Для этого в runtime есть тип Pinner (начиная с Go 1.21) с методами Pin(ptr) и Unpin. Соответственно, в процессе извлечения указателей они закрепляются в памяти:
ptr := unsafe.SliceData(data[i])
// Сообщает сборщику мусора, что указатель ptr используется вне Go
pinner.Pin(ptr)
Указатели закрепляются по одному, зато открепляются все чохом:
// вернуть указатели под управление сборщиком мусора
pinner.Unpin()
Результат работы программы:
Number of rows: 4
1.000000 2.000000 3.000000 4.000000
1.100000 2.100000 3.100000 4.100000
1.200000 2.200000 3.200000 4.200000
1.300000 2.300000 3.300000 4.300000