Конвертация из []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 шт):

Автор решения: Pak Uula

Нужно ручками собрать массив указателей.

Вот пример:

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 
→ Ссылка