Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request for Flutter FFI and Golang tutorials. #3

Open
ganeshrvel opened this issue Jul 27, 2020 · 19 comments
Open

Request for Flutter FFI and Golang tutorials. #3

ganeshrvel opened this issue Jul 27, 2020 · 19 comments

Comments

@ganeshrvel
Copy link

Dart has recently added async callback support for FFI. There aren't much instructions of how to get it done though.

It would be great if you could give us an example code which could get us started on async calls to golang and callbacks from go to dart using FFI.

I had appreciate any sort of help.

@archanpaul
Copy link
Owner

This might help you to get started. https://pub.dev/packages/dexecve

@ganeshrvel
Copy link
Author

It's not exactly FFI. Looks like it's some sort of hacky way of calling a native executable.

It involve:

  1. a separate executable which is written in Dart/golang and it uses FFI to connect them.
  2. use the 'dexecve' which is equivalent to spawn to access the first executable.

I have been trying to integrate golang into my flutter app for a very long time now, but I am not able to figure out nativeport support for golang

@archanpaul
Copy link
Owner

Yes it is a hack .. but has some indications about how to proceed in current scenario.
AFAIK right now dart FFI only supports C, C++. Hence, you need to build a C shared library from your Go code and then access the exported Go functions ( as C functions ) from Dart FFI.
If I get some time, I will write a tutorial about it.

@ganeshrvel
Copy link
Author

ganeshrvel commented Jul 27, 2020

AFAIK right now dart FFI only supports C, C++. Hence, you need to build a C shared library from your Go code and then access the exported Go functions ( as C functions ) from Dart FFI.

Accessing go native functions from dart is pretty easy but unfortunately that will block the main event thread. Recently dart added a way to execute the native calls in async way which also gives us callback feature (native code calls a dart function) and this one seems to be bit trickier.

I have been trying to get it done but I get stuck at the native port.

If I get some time, I will write a tutorial about it.

Thank you. Much appreciated. Do tag me here if that happens.

Thanks again. Have a nice one

@ganeshrvel
Copy link
Author

There is a new repo which explains how to get this done.
https://github.com/mraleph/go_dart_ffi_example

@archanpaul archanpaul reopened this Aug 7, 2020
@archanpaul
Copy link
Owner

https://pub.dev/packages/ffigen is quite helpful. I use this for generating code from c header which is generated from Go. Also refer to golang/go#35715

@ganeshrvel
Copy link
Author

Awesome!

@archanpaul
Copy link
Owner

One of our app https://play.google.com/store/apps/details?id=plus.fitzy.fitzy_app uses these extensively.

@ganeshrvel
Copy link
Author

I have a question though. Is there anyway to pack a static library along with go?

For compiling a program in Go i need to have libusb pre installed on my mac. The app runs fine if I install libusb via brew. But when I try to run the app in a different machine it says the static library is missing.

Is there a way to bundle the dylib file of a library along with the Go program? Checked everywhere couldnt get this working

@archanpaul
Copy link
Owner

I didn't try that. First create a flutter plugin with cmake build and then use the plugin through Dart class. There are some interesting discussions available here.

@ganeshrvel
Copy link
Author

I have a question though. Is there anyway to pack a static library along with go?

Actually this is not for a Flutter app. I need it for a executable that I am building for one of my projects.

Thanks anyway!

@archanpaul
Copy link
Owner

archanpaul commented Aug 10, 2020 via email

@ganeshrvel
Copy link
Author

Hey, thanks for the quick reply.

I tried to allocate struct to malloc and then send the pointer as int64 to Dart. And then in the dart side I tried to print the value of message returned from Go.

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
Copy link
Author

ganeshrvel commented Aug 10, 2020

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!

@archanpaul
Copy link
Owner

archanpaul commented Aug 10, 2020 via email

@archanpaul
Copy link
Owner

archanpaul commented Aug 10, 2020 via email

@skyred
Copy link

skyred commented Feb 28, 2022

The repo tutorial uses Gomobile for Android and iOS as (#2 indicates this solution won't work for Desktop). Does this new direction of using Dart FFI sound like a solution would work for both mobile and desktops?

@archanpaul
Copy link
Owner

archanpaul commented Feb 28, 2022 via email

@zzxap
Copy link

zzxap commented Apr 19, 2023

GetWork declare in which file??

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants