go_dart_ffi_example icon indicating copy to clipboard operation
go_dart_ffi_example copied to clipboard

How to return a struct from a Go function to Dart?

Open ganeshrvel opened this issue 5 years ago • 3 comments

Is it possible to send back a struct from the Go function to Dart?

Could you add an example to send a struct from Go to Dart and how to handle the received struct in the Dart side?

ganeshrvel avatar Aug 09 '20 11:08 ganeshrvel

The simplest way to send a struct over is to allocate it with C.malloc and then send the pointer to it as an int64 to Dart. On the Dart side you can use Pointer<...>.fromAddress to create a pointer object and then work with the struct.

mraleph avatar Aug 09 '20 21:08 mraleph

Hey, thanks for the quick reply.

Like you said, I tried to allocate struct to malloc and then send the pointer as int64 to Dart.

But Dart is picking the message from Go as the int 5 which basically (pWork->a=5;)

dart_api_dl.go:

// // Go does not allow calling C function pointers directly. So we are
// // forced to provide a trampoline.
// bool GoDart_PostCObject(Dart_Port_DL port, Dart_CObject* obj) {
//   return Dart_PostCObject_DL(port, obj);
// }
//
//	typedef struct Work{
//		int64_t a;
//		int64_t b;
//	}Work;
//
//	void GetWork(void **ppWork) {
//		Work *pWork= (Work *)malloc(sizeof(Work));
//		pWork->a=5;
//		pWork->b=6;
//		*ppWork = pWork;
//	}
import "C"

func Init(api unsafe.Pointer) C.long {
	return C.Dart_InitializeApiDL(api)
}

type Work struct {
	a int64
	b int64
}

func SendToPort(port int64, msg int64) {
	var obj C.Dart_CObject
	obj._type = C.Dart_CObject_kInt64

	var pwork unsafe.Pointer
	C.GetWork(&pwork)
	work := *(*Work)(pwork)

	unsafePtr := (*int64)(unsafe.Pointer(&work))

	*(*C.int64_t)(unsafe.Pointer(&obj.value[0])) = C.int64_t(*unsafePtr)

	C.GoDart_PostCObject(C.int64_t(port), &obj)
}

Dart side:

  Future<void> run() async {
    _squashArchiverLib.InitializeDartApi(NativeApi.initializeApiDLData);

    final interactiveCppRequests = ReceivePort();

    final interactiveCppSub = interactiveCppRequests.listen((data) {

      print(data); // <---------outputs 5---------->

      /*final work = Pointer<Work>.fromAddress(data as int);
      print(work.ref.a);
      print(work.ref.b);*/
    });

    final nativePort = interactiveCppRequests.sendPort.nativePort;

    _squashArchiverLib.StartWork(nativePort);
  }

Git repo: https://github.com/ganeshrvel/squash_archiver/blob/feature/go-archiver-example/packages/archiver_ffi/native/archiver_lib/dart_api_dl/dart_api_dl.go

I am not able to return the pointer address of the struct back to Dart, always the value of the struct is returned. Am I doing it right? Any suggestions will be appreciated.

ganeshrvel avatar Aug 10 '20 10:08 ganeshrvel

I got it working. If anyone else is looking for an answer here, this is what i did. I first did a cast of the struct ptr using uint64_t in the GetWork and returned the value back.

//	int64_t GetWork(void **ppWork) {
//		Work *pWork= (Work *)malloc(sizeof(Work));
//		pWork->a=16;
//		pWork->b=15;
//		*ppWork = pWork;
//
//		int64_t ptr = (int64_t)pWork;
//
//		return ptr;
//	}
func SendToPort(port int64, msg int64) {
	var obj C.Dart_CObject
	obj._type = C.Dart_CObject_kInt64

	var pwork unsafe.Pointer
	ptrAddr := C.GetWork(&pwork)

	*(*C.int64_t)(unsafe.Pointer(&obj.value[0])) = ptrAddr

	C.GoDart_PostCObject(C.int64_t(port), &obj)

	defer C.free(pwork)
}

Dart side:

    final interactiveCppSub = interactiveCppRequests.listen((data) {
      final work = Pointer<Work>.fromAddress(data as int);
      print(work.ref.a);
      print(work.ref.b);
    });

Thanks a lot for the suggestions. You had been a great help!

ganeshrvel avatar Aug 10 '20 10:08 ganeshrvel