Как вызвать c-функцию, которая принимает c-структуру, содержащую указатели - PullRequest
0 голосов
/ 29 мая 2019

Из программы GO на Raspberry PI я пытаюсь вызвать функцию (функция Matlab, преобразованная в функцию C), а вход в функцию - указатель на структуру, а структура содержит указатель на double (данные)и указатель на int (размер) и два типа int (allocSize, numDimensions).Я пробовал несколько способов, но ничего не получалось, когда я проходил компиляцию, она обычно выдает панику: ошибка времени выполнения: аргумент cgo имеет указатель Go на указатель Go, когда я запускаю программу.

sumArray.c

/*sumArray.C*/
/* Include files */
#include "sumArray.h"

/* Function Definitions */
double sumArray(const emxArray_real_T *A1)
{
  double S1;
  int vlen;
  int k;
  vlen = A1->size[0];
  if (A1->size[0] == 0) {
    S1 = 0.0;
  } else {
    S1 = A1->data[0];
    for (k = 2; k <= vlen; k++) {
      S1 += A1->data[k - 1];
    }
  }

  return S1;
}

sumArray.h

#ifndef SUMARRAY_H
#define SUMARRAY_H

/* Include files */
#include <stddef.h>
#include <stdlib.h>
#include "sumArray_types.h"

/* Function Declarations */
extern double sumArray(const emxArray_real_T *A1);

#endif

sumArray_types.h

#ifndef SUMARRAY_TYPES_H
#define SUMARRAY_TYPES_H

/* Include files */

/* Type Definitions */
#ifndef struct_emxArray_real_T
#define struct_emxArray_real_T

struct emxArray_real_T
{
  double *data;
  int *size;
  int allocatedSize;
  int numDimensions;
};

#endif                                 /*struct_emxArray_real_T*/

#ifndef typedef_emxArray_real_T
#define typedef_emxArray_real_T

typedef struct emxArray_real_T emxArray_real_T;

#endif                                 /*typedef_emxArray_real_T*/
#endif

/* End of code generation (sumArray_types.h) */

main.go

// #cgo CFLAGS: -g -Wall
// #include <stdlib.h>
// #include "sumArray.h"
import "C"

import (
   "fmt"
)
func main() {
   a1 := [4]C.Double{1,1,1,1}
   a2 := [1]C.int{4}
   cstruct := C.emxArray_real_T{data: &a1[0], size: &a2[0]}
   cstructArr := [1]C.emxArray_real_T{cstruct}
   y := C.sumArray(&cstructArr[0])
   fmt.Print(float64(y))
}

В этом примере я получаю панику: ошибка времени выполнения: аргумент cgo имеет указатель Go на указатель Go, когда я запускаю программу.

Я не знаю, как заставить это работать или возможно ли заставить это работать.Я надеюсь, что кто-то может мне помочь или дать какое-нибудь указание, как решить эту проблему.

1 Ответ

0 голосов
/ 11 июня 2019

Слишком много для комментария, поэтому вот ответ.

Во-первых, оригинальный текст:

Прямым решением является использование C.malloc(4 * C.sizeof(C.double)) для выделения массива double -s. Обратите внимание, что вы должны обязательно позвонить C.free(), когда закончите. То же самое относится ко второму массиву одного int.

Теперь, ваш комментарий к замечанию Маттани, который был немного переформатирован:

спасибо, что дали несколько указателей. Я пробовал с

a1 := [4]C.double{1,1,1,1}
sizeA1 := C.malloc(4 * C.sizeof_double)
cstruct := C.emxArray_real_T{
  data: &a1[0],
  size: (*C.int)(sizeA1)
}
y := C.sumArray(cstruct)
defer C.free(sizeA1)

но это дало мне то же самое ответ как и прежде аргумент cgo имеет указатель Go на указатель Go, когда я попытался запустить программу

Кажется, вы все еще упускаете важный момент. Когда вы используете cgo, существует два непересекающихся"представления памяти":

  • «Память Go» - это все, что выделяется средой выполнения Go для запуска вашего рабочего процесса - от имени этого процесса. Эта память (большую часть времени, за исключением странных уловок) известна GC, которая является частью времени выполнения.

  • «Память C» - это память, выделенная кодом C - обычно путем вызова libc s malloc() / realloc().

А теперь представьте не слишком надуманный сценарий:

  1. Ваша программа запускается, "сторона" C инициализируется и порождает собственную нить (или нити) и удерживает на них дескрипторы.
  2. Ваша сторона Go уже использует несколько потоков для запуска ваших подпрограмм.
  3. Вы выделяете часть памяти Go в своем коде Go и передаете ее на сторону С.
  4. Сторона C передает адрес этой памяти одному или нескольким собственным потокам.
  5. Ваша программа продолжает пыхтеть, , как и потоки на стороне C - параллельно с вашим кодом Go.

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

Также учтите, что Go значительно более высокоуровневый язык программирования, чем C; Как минимум, он имеет автоматическую сборку мусора и заметит, что в спецификации спецификации Go ничего не указано, как точно должен быть указан GC. Это означает, что конкретная реализация Go (включая эталонную - в будущем) может свободно разрешать своему ГХ перемещать произвольные объекты в памяти¹, и это означает обновление каждого указателя, указывающего на блок памяти в его первоначальное местоположение, указывающее на то же место в новом месте блока - после того, как оно было перемещено.

Учитывая эти соображения, разработчики Go постулировали, что для того, чтобы cgo -использовать программы, пригодные для будущего², запрещено передавать на C любые блоки памяти, которые содержат указатели на другие блоки памяти Go.

Впрочем, можно передавать блоки памяти Go, содержащие указатели, в память C.

Возвращаясь к примеру из вашего второго комментария, Вы по-прежнему выделяете массив 4 double с a1 в памяти Go.

Затем оператор cstruct := C.emxArray_real_T{...} снова выделяет экземпляр C.emxArray_real_T в памяти Go и поэтому после инициализации его поля data указателем на память Go (&a1[0]), а затем передает его адрес на стороне C среда выполнения выполняет свои динамические проверки перед тем, как на самом деле вызвать сторону C и вывести из строя вашу программу.


¹ Это типичное поведение для так называемых сборщиков мусора, например,

² То есть вы перекомпилируете свою программу с будущей версией компилятора Go того же «основного» выпуска, и программа продолжает работать без изменений.

...