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}
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 <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;
// }
//
//
import "C"
type PredictResult C.struct_PredictResult
func main() {
p := C.getOneResult()
if p == nil {
log.Fatal("got nil")
}
pp := (*PredictResult)(p)
fmt.Println(pp)
parr := C.getTenResults()
if parr == nil {
log.Fatal("got nil")
}
pslice := (*[1 << 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 "C"
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 "C"
And then in Go do:
prslice := make([]PredictResult, 10)
C.PopulateTenResults(unsafe.Pointer(&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.