cloudpeers / ffi-gen

Call rust from any language.
20 stars 5 forks source link

How to call dart function from rust(callback) #55

Closed canewsin closed 2 years ago

canewsin commented 2 years ago

Previously with my implemetation I passed a pointer to rust function from dart, rust make the callback when needed and invoke such function, how to run a callback with this lib.

dvc94ch commented 2 years ago

Callbacks in dart have to be called synchronously. For asynchronous notifications a SendPort needs to be used. Futures and Streams use SendPort to notify dart when they're ready to be polled. If you need to go with a callback style API the way to do it would be using Future.then method which will be called when the future completes.

canewsin commented 2 years ago

@dvc94ch One More Thing Could Provide What Rust Types are Supported for Code Gen?

canewsin commented 2 years ago

Callbacks in dart have to be called synchronously. For asynchronous notifications a SendPort needs to be used. Futures and Streams use SendPort to notify dart when they're ready to be polled. If you need to go with a callback style API the way to do it would be using Future.then method which will be called when the future completes.

How to pass a Sync Function, could you provide some example?

dvc94ch commented 2 years ago

don't quite follow. the idea is to use futures and streams with the flutter FutureBuilder and StreamBuilder. how exactly are you using callbacks?

canewsin commented 2 years ago

Previously I did like this Dart :

  late final _handle_eventPtr = _lookup<
      ffi.NativeFunction<
          ffi.Int32 Function(
              ffi.Pointer<
                  ffi.NativeFunction<
                      ffi.Void Function(ffi.Pointer<Utf8>)>>)>>('event_loop');
  late final _handle_event = _handle_eventPtr.asFunction<
      int Function(
          ffi.Pointer<
              ffi.NativeFunction<ffi.Void Function(ffi.Pointer<Utf8>)>>)>();

  int handleEventGen(
    ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<Utf8>)>>
        callback,
  ) {
    return _handle_event(
      callback,
    );
  }
pub extern "C" fn event_loop(callback: extern "C" fn(line: *const c_char)) -> u8 {
    let runtime = unsafe { RUNTIME.as_mut() };
    if let None = config {
        return 4 as u8;
    }
    let config = config.unwrap();
    .....
    match (runtime, swarm) {
        (Some(runtime), Some(swarm)) => {
            runtime.block_on(async move {
                loop {
                   ...
                   callback(something_here)// This is continue to run;
                }
            });
            0 as u8
        }
        (None, None) => 1 as u8,
        (Some(_), None) => 2 as u8,
        (None, Some(_)) => 3 as u8,
    }
}

calling :

void callback(Pointer<Utf8> str) {
  // print(Utf8.fromUtf8(str));
  print(str.toDartString());
}

void attachListener() {
  var result = handleEventGen(Pointer.fromFunction(cb));
  var result = 0;
  if (result == 0) {
    print("Success");
  }
}
dvc94ch commented 2 years ago

well you can do that much easier now. swarm implements stream so you can do something like the following:

object SwarmEvent {
    fn to_string() -> String;
}
object Swarm {
    fn subscribe() -> Stream<SwarmEvent>;
}
pub struct SwarmEvent(libp2p_swarm::SwarmEvent);

impl SwarmEvent {
    pub fn to_string(&self) -> String {
        ...
   }
}

pub struct Swarm(libp2p_swarm::Swarm);

impl Swarm {
    pub fn subscribe(&self) -> impl Stream<Item = SwarmEvent> {
        ...
    }
}
StreamBuilder(
    stream: swarm.subscribe(),
    builder: (context, snapshot) {
        if (snapshot != null) {
            return Text(snapshot.toString());
        } else {
            return Text("loading");
        }
    }
dvc94ch commented 2 years ago

Does that help? closing for now

canewsin commented 2 years ago

Since I was just started with Rust, I had zero knowledge about streams tried to browse some examples nothing helped.

dvc94ch commented 2 years ago

well what you want to do is spawn the swarm in a background task (because polling it requires &mut self) using something like this:

let (tx, rx) = futures::mpsc::unbounded();
async_global_executor::spawn(async move {
    let mut listeners = vec![];
    loop {
    futures::select! {
        res = rx.next() => {
           if let Some(ch) = res {
               listeners.push(ch);
           }
       }
       res = swarm.next() => {
           if let Some(event) = res {
               listeners.retain(|tx| tx.unbounded_send(event.clone()).is_ok());
           }
       }
    }}
});
canewsin commented 2 years ago
object SwarmEventExt {
    fn to_string() -> string;
}
object SwarmExt {
    fn subscribe() -> Stream<SwarmEventExt>;
}
pub struct SwarmEventExt(NetworkSwarmEvent);

impl SwarmEventExt {
    pub fn to_string(&self) -> String {
        format!("{:?}", self.0)
    }
}

///Is this required ?
impl Stream for SwarmEventExt {
    type Item = SwarmEventExt;
    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
       ///Not figured how to implemen.
        // Poll::Ready(Some(SwarmEventExt(self.0.poll_next(cx)?)))
        Poll::Ready(None)
    }
}

pub struct SwarmExt(Swarm<DecentNetworkBehaviour>);
use libp2p::futures::Stream;
// use streams::SwarmEventExt;
impl SwarmExt {
                                      ///it's our custom type right ?
    pub fn subscribe(&mut self) -> impl Stream<Item = SwarmEvent> {
        //impl Stream<Item = SwarmEventExt>
        let runtime = tokio::runtime::Runtime::new();
        let swarm: Swarm<DecentNetworkBehaviour> = self.0;
        let s = runtime.unwrap().block_on(async {
            let a = swarm.select_next_some().await;
        });        unimplemented!()
    }
}
canewsin commented 2 years ago

oh just seen your comment