将结构体数组从C++传递给GO。

huangapple go评论88阅读模式
英文:

Pass array of structs from C++ to GO

问题

我正在尝试将一个由C++生成的TensorFlow盒子预测数组传递给Golang,但无论我怎么做都无法成功。我有一个Golang程序,调用一个使用cgo在C++中进行TensorFlow检测的函数。这一切都正常工作,我能够在C++中获取到预测结果。问题是如何将这些预测结果作为一个包含100个结构体的数组传递给Golang。

我能够在Golang中设置一个指针,并使用该指针地址在C++中设置一个结构体。下面是这部分代码:

type PredictResult struct {
    Loc   [4]float32
    Score int
    Label int
}

var predictions PredictResult
predictions_ptr := unsafe.Pointer(&predictions)
C.LIB_predict(predictions_ptr)

fmt.Println("GO predictions: ", predictions)

bridge.hpp

struct PredictResult{
    float Loc[4];
    int64_t Score;
    int64_t Label;
};

void LIB_predict(void* predictions);

bridge.cpp

void LIB_predict(void* predictions){

    PredictResult *p = (PredictResult*)predictions;
    p->Score = 6;
    p->Label = 95;
}

打印结果为:

GO predictions: {[0 0 0 0] 6 95}

我想在C++中设置一个结构体数组,并在Golang中获取这个数组。我认为只需要使用之前的指针地址作为C++数组的地址,然后在Golang中通过指针恢复结构体即可。有人对此有解决方案吗?

英文:

I'm trying to get an array of tensorflow box predictions from C++ to golang, but I'm not able to do it no matter what I do. I have a GO program that calls a function that does tensorflow detections in C++ using cgo. This all works and I'm able to get the predictions in C++. The problem is to transfer these predictions into GO as an array of 100 structs that each hold one prediction.

I'm able to set a pointer in GO and use this pointer address to set one struct in C++. The code for this is seen below.

I want to set an array of structs in C++ and retreive this array in GO. I thought it should be easy to just use the same pointer address as earlier and use this as the address for my C++ array. Then I could restore the struct from the pointer in GO. Does anyone have a solution for this?

GO

type PredictResult struct {
	Loc   [4]float32
	Score int
	Label int
}

var predictions PredictResult
predictions_ptr := unsafe.Pointer(&predictions)
C.LIB_predict(predictions_ptr)

fmt.Println("GO predictions; ", predictions)

bridge.hpp

struct PredictResult{
	float Loc[4];
	int64_t Score;
	int64_t Label;
};

void LIB_predict(void* predictions);

bridge.cpp

void LIB_predict(void* predictions){

PredictResult *p = (PredictResult*)predictions;
    p->Score = 6;
    p->Label = 95;
}

Prints:

GO predictions;  {[0 0 0 0] 6 95}

答案1

得分: 2

假设你的C函数返回类型为PredictResult*的数组,并且假设你知道返回数组的长度(在下面的示例中,我假设长度为10,但你可以根据实际情况进行替换),这种方法应该可以工作:

#include <stdio.h>
#include <stdlib.h>

typedef struct PredictResult {
    float Loc[4];
    int64_t Score;
    int64_t Label;
} PredictResult;

PredictResult* getOneResult() {
  PredictResult* p = (PredictResult*)calloc(1, sizeof(PredictResult));
  p->Score = 10;
  p->Label = 99;
  p->Loc[1] = 2.5;
  p->Loc[3] = 3.5;
  return p;
}

PredictResult* getTenResults() {
  PredictResult* parr = (PredictResult*)calloc(10, sizeof(PredictResult));
  parr[0].Score = 10;
  parr[0].Label = 99;
  parr[0].Loc[1] = 2.5;
  parr[0].Loc[3] = 3.5;

  parr[4].Score = 44;
  parr[4].Label = 123;
  parr[4].Loc[1] = 12.25;
  parr[4].Loc[3] = -40.5;
  return parr;
}

你最感兴趣的是如何将getTenResults的结果转换为适当结构类型的Go切片。这里使用了Go维基上推荐的技术。

根据你的C函数的确切签名,你可能需要在import "C"部分编写一个“桥接”函数,以便将数据方便地提供给Go,但这是基本的要点。

作为另一种选择,如果你希望在Go端分配切片并传入指向C的指针进行填充,可以这样做:

void PopulateTenResults(void* arr) {
  PredictResult* parr = (PredictResult*)arr;
  parr[1].Score = 210;
  parr[1].Label = 299;
  parr[1].Loc[1] = 22.5;
  parr[1].Loc[3] = 23.5;

  parr[8].Score = 344;
  parr[8].Label = 3123;
  parr[8].Loc[1] = 312.25;
  parr[8].Loc[3] = -340.5;
}

然后在Go中执行以下操作:

prslice := make([]PredictResult, 10)
C.PopulateTenResults(unsafe.Pointer(&prslice[0]))
fmt.Println(prslice)

当然,这里的硬编码的10只是为了简单起见;你可以将arr的长度作为参数传递给C函数。

英文:

Assuming your C function returns the array as PredictResult* and assuming you know the length of the returned array (in the example below I assume 10, but you can replace it by whatever works), this approach should work:

// #include &lt;stdio.h&gt;
// #include &lt;stdlib.h&gt;
//
// typedef struct PredictResult {
//     float Loc[4];
//     int64_t Score;
//     int64_t Label;
// } PredictResult;
//
// PredictResult* getOneResult() {
//   PredictResult* p = (PredictResult*)calloc(1, sizeof(PredictResult));
//   p-&gt;Score = 10;
//   p-&gt;Label = 99;
//   p-&gt;Loc[1] = 2.5;
//   p-&gt;Loc[3] = 3.5;
//   return p;
// }
//
// PredictResult* getTenResults() {
//   PredictResult* parr = (PredictResult*)calloc(10, sizeof(PredictResult));
//   parr[0].Score = 10;
//   parr[0].Label = 99;
//   parr[0].Loc[1] = 2.5;
//   parr[0].Loc[3] = 3.5;
//
//   parr[4].Score = 44;
//   parr[4].Label = 123;
//   parr[4].Loc[1] = 12.25;
//   parr[4].Loc[3] = -40.5;
//   return parr;
// }
//
//
import &quot;C&quot;

type PredictResult C.struct_PredictResult

func main() {
	p := C.getOneResult()
	if p == nil {
		log.Fatal(&quot;got nil&quot;)
	}

	pp := (*PredictResult)(p)
	fmt.Println(pp)

	parr := C.getTenResults()
	if parr == nil {
		log.Fatal(&quot;got nil&quot;)
	}

	pslice := (*[1 &lt;&lt; 28]PredictResult)(unsafe.Pointer(parr))[:10:10]
	fmt.Println(pslice)
}

What you'll be most interested in is how the result of getTenResults is converted to a Go slice of the appropriate struct type. This is employing the technique recommended on the Go wiki.

Depending on the exact signature of your C function you may need to write a "bridge" function in the import &quot;C&quot; part to provide the data as convenient to Go, but this is the basic gist of it.

As an alternative, if you wish to allocate the slice on the Go side and pass in a pointer to C to populate, you can do this:

// void PopulateTenResults(void* arr) {
//   PredictResult* parr = (PredictResult*)arr;
//   parr[1].Score = 210;
//   parr[1].Label = 299;
//   parr[1].Loc[1] = 22.5;
//   parr[1].Loc[3] = 23.5;
//
//   parr[8].Score = 344;
//   parr[8].Label = 3123;
//   parr[8].Loc[1] = 312.25;
//   parr[8].Loc[3] = -340.5;
// }
//
//
import &quot;C&quot;

And then in Go do:

prslice := make([]PredictResult, 10)
C.PopulateTenResults(unsafe.Pointer(&amp;prslice[0]))
fmt.Println(prslice)

Of course the hard-coded 10 is just for simplicity here; you could pass the length of arr as a parameter to C.

答案2

得分: 1

你可以将切片的第一个元素的指针和切片的长度传递给C++,并将其视为C风格的数组。

英文:

You can pass a pointer to the first element in a slice and the length of the slice to C++ and treat it like a C-style array.

huangapple
  • 本文由 发表于 2021年7月1日 04:52:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/68201487.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定