Hello I am using dart:ffi to build an interface with my native c/c++ library.
and I needed a way to get a callback from c to dart as an example in sqlite:
int sqlite3_exec(
sqlite3*, /* An open database */
const char *sql, /* SQL to be evaluated */
int (*callback)(void*,int,char**,char**), /* Callback function */
void *, /* 1st argument to callback */
char **errmsg /* Error msg written here */
);
the third parameter in sqlite3_exec is function pointer to a callback.
so if I called this function in dart using ffi I need to pass a function pointer: and in dart:ffi Pointer class there is a function named fromFunction witch accepts a dart static function and an exceptionalReturn; but just by calling this function to get the function pointer of a dart managed function: a (sigterm) is raised and the dart code no long work in the process.
So My Question: Is there any way to get a native callback in dart, as in Python, c#, ..
Extra:
Is there any way to include dartino in a flutter project, since this ForeignDartFunction covers what I need.
I got an example to work. Hopefully you can adapt this to your case.
Example C function
EXTERNC int32_t foo(
int32_t bar,
int32_t (*callback)(void*, int32_t)
) {
return callback(nullptr, bar);
}
Dart code
First the typedefs. We need two for the native function foo and one for the Dart callback.
typedef example_foo = Int32 Function(
Int32 bar, Pointer<NativeFunction<example_callback>>);
typedef ExampleFoo = int Function(
int bar, Pointer<NativeFunction<example_callback>>);
typedef example_callback = Int32 Function(Pointer<Void>, Int32);
and the code for the callback
static int callback(Pointer<Void> ptr, int i) {
print('in callback i=$i');
return i + 1;
}
and the lookup
ExampleFoo nativeFoo =
nativeLib.lookup<NativeFunction<example_foo>>('foo').asFunction();
and, finally, use it like this:
int foo(int i) {
return nativeFoo(
i,
Pointer.fromFunction<example_callback>(callback, except),
);
}
as expected, foo(123) prints flutter: in callback i=123 and returns 124
Related
This post is a duplicate of the Github Issue here.
dart --version
Dart SDK version: 2.15.0-116.0.dev (dev) (Thu Sep 16 09:47:01 2021 -0700) on "linux_x64"
I've been looking up examples for callbacks and I have tried to get callbacks working for me in FFI.
My current situation
I have a function in my library which expects a pointer to a function. The bindings for the same generated by ffigen seem correct to me.
int SetCallback(
CallbackType callback,
) {
return _SetCallback(
callback,
);
}
late final _SetCallbackPtr =
_lookup<NativeFunction<Int32 Function(CallbackType)>>(
'SetCallback');
late final _SetCallback =
_SetCallbackPtr.asFunction<int Function(CallbackType)>();
where, typedef CallbackType = Pointer<NativeFunction<Void Function(Uint32)>>;.
What I want to do here is to setup this callback in Dart, pass it to the FFI, essentially using it as my callback as I would have in C. In my API which abstracts away from FFI code (which means I have a class MyLibrary full of static functions that the user will call directly, which in turn calls functions from an object _nativeLibrary of the class MyNativeLibrary I have created), I have:
static int SetCallback({required CallbackFuncDart callback}) {
Pointer<NativeFunction<CallbackFunc>> pointer = Pointer.fromFunction(callback);
int status = _nativeLibrary.SetCallback(
pointer,
);
if (STATUS_OK != status) {
throw LibLexemeException(status);
}
return status;
}
typedef CallbackFunc = Void Function(Uint32);
typedef CallbackFuncDart = void Function(int);
While the sqlite ffi example states here that
Features which dart:ffi does not support yet:
Callbacks from C back into Dart.
I believe the docs haven't been updated to reflect the changes at the samples here. The samples haven't been very clear due to them not having any C/C++ files, or an idea of how the C functions work. Even so, I think this example contains a segment(last code block) where a Dart function is being passed as a callback which I have replicated in my program. It is not clear to me how this will work but upon trying to compile my program I get:
ERROR: ../lib/library_lexeme.dart:180:74: Error: fromFunction expects a static function as parameter. dart:ffi only supports calling static Dart functions from native code. Closures and tear-offs are not supported because they can capture context.
ERROR: Pointer<NativeFunction<CallbackFunc>> pointer = Pointer.fromFunction(callback);
The short version is that you can't pass your callnback as an argument:
static int SetCallback({required CallbackFuncDart callback}) {
Pointer<NativeFunction<CallbackFunc>> pointer = Pointer.fromFunction(callback); // <-- this isn't considered a static function
It's quite annoying but you must use a static function defined ahead of time for your dart callbacks to be called from C.
Apparently for now only static functions can be passed via ffi. But if you have to access an instance's data and you're sure that the instance exists you can use my workaround. I use a static list to the instances. This is stupid and ugly but it works for me:
class CallbackClass {
static Int8 classCallback(int id) {
final instance = instanceList[id];
return instance.instanceCallback();
}
Int8 instanceCallback() { return instanceId; }
static List<CallbackClass> instanceList = <CallbackClass>[];
late final int instanceId;
CallbackClass {
instanceId = instanceList.length;
instanceList.insert(instanceId, this);
myFFImapping.passCallback(instanceId, Pointer.fromFunction<>(classCallback);)
}
}
I omitted the necessary c code, FFI mapping and casting to correct types for clarity, so it obviously won't compile like this.
from the call back function of sys.settrace how to pass frame object to a c++ which accepts void *
Limitations (in a given situation):
Forced to use sys.settrace (can not use PyEval_SetTrace)
Also c++ function can not accept PyObject* or PyFrameObject*
C++ code (using SWIG python bindings are generated for this):
class TEST_DECLS InterpPython{
static int TraceHook(void *frame, hwString what, void * arg);
}
Python code:
sys.settrace(_trace_hook)
def _trace_hook(frame, event, arg):
InterpPython_TraceHook(frame,event, arg)
Results:
TypeError: in method 'InterpPython_TraceHook', argument 1 of type 'void *'
Try a typemap to convert the Python input object to a void* when it is a void* frame parameter. You could also add a check that the object is actually a PyFrameObject.
%typemap(in) void* frame %{
$1 = (void*)$input;
%}
In a C++/CLI assembly, I'm trying to call a managed delegate from a native callback. I followed Doc Brown's answer here, and my implementation so far looks like this:
The native callback - ignore the commented out parts for now:
static ssize_t idaapi idb_callback(void* user_data, int notification_code, va_list va)
{
switch (notification_code)
{
case idb_event::byte_patched:
{
//ea_t address = va_arg(va, ea_t);
//uint32 old_value = va_arg(va, uint32);
return IdaEvents::BytePatched(0, 0);
}
break;
}
return 0;
}
As you can see above, I call this managed delegate instantiated in a static class:
public delegate int DatabaseBytePatchedHandler(int address, int originalValue);
private ref class IdaEvents
{
static IdaEvents()
{
BytePatched = gcnew DatabaseBytePatchedHandler(&OnDatabaseBytePatched);
}
public: static DatabaseBytePatchedHandler^ BytePatched;
private: static int OnDatabaseBytePatched(int address, int originalValue)
{
return 0;
}
};
This compiles fine. But the code is incomplete - remember the commented out part in the native callback above? I actually have to retrieve the values from the va_list passed to the callback, and pass those on to my managed delegate:
ea_t address = va_arg(va, ea_t);
uint32 old_value = va_arg(va, uint32);
return IdaEvents::BytePatched(address, old_value);
But as soon as I uncomment one of the lines using va_arg, I cannot compile the project anymore and retrieve the following errors marking the line where I call the managed delegate:
C3821 'IdaEvents': managed type or function cannot be used in an unmanaged function
C3821 'IdaEvents::BytePatched': managed type or function cannot be used in an unmanaged function
C3821 'BytePatched': managed type or function cannot be used in an unmanaged function
C3821 'DatabaseBytePatchedHandler::Invoke': managed type or function cannot be used in an unmanaged function
C3642 'int DatabaseBytePatchedHandler::Invoke(int,int)': cannot call a function with __clrcall calling convention from native code
C3175 'DatabaseBytePatchedHandler::Invoke': cannot call a method of a managed type from unmanaged function 'idb_callback'
This really confuses me. Why is the compiler suddenly acting up as soon as I try to use va_arg? Even a single line without any assignment causes this error to pop up.
Am I thinking too naive here? I'm obviously missing a piece of the puzzle, and any help supporting me in finding it is greatly appreciated.
I've just started making libraries in Arduino. I've made a library named inSerialCmd. I want to call a function named delegate() that is defined in the main program file, stackedcontrol.ino, after the inSerialCmd library is included.
When I try to compile, one error is thrown:
...\Arduino\libraries\inSerialCmd\inSerialCmd.cpp: In member function
'void inSerialCmd::serialListen()':
...\Arduino\libraries\inSerialCmd\inSerialCmd.cpp:32: error:
'delegate' has not been declared
After doing a bit of searching, it seemed that adding the scope resolution operator might do the trick. So I added the "::" before delegate(), now "::delegate()", but the same error is thrown.
Now I'm stumped.
You cannot and should not directly call a function in a program from a library. Keep in mind a key aspect that makes a library into a library:
A library does not depend on the specific application. A library can be fully compiled and packaged into the .a file without the existence of a program.
So there is a one way dependency, a program depends on a library. This at first glance may seem to prevent you from achieving what you want. You can achieve the functionality you are asking about through what is sometimes referred to as a callback. The main program would provide to the library at runtime a pointer to the function to execute.
// in program somwehere
int myDelegate(int a, int b);
// you set this to the library
setDelegate( myDelegate );
You see this in the arduino if you look at how interrupt handlers are installed. This same concept exists in many environments - event listeners, action adapters - all with the same goal of allowing a program to define the specific action that a library cannot know.
The library would store and call the function via the function pointer. Here is a rough sketch of what this looks like:
// in the main program
int someAction(int t1, int t2) {
return 1;
}
/* in library
this is the delegate function pointer
a function that takes two int's and returns an int */
int (*fpAction)(int, int) = 0;
/* in library
this is how an application registers its action */
void setDelegate( int (*fp)(int,int) ) {
fpAction = fp;
}
/* in libary
this is how the library can safely execute the action */
int doAction(int t1, int t2) {
int r;
if( 0 != fpAction ) {
r = (*fpAction)(t1,t2);
}
else {
// some error or default action here
r = 0;
}
return r;
}
/* in program
The main program installs its delegate, likely in setup() */
void setup () {
...
setDelegate(someAction);
...
I register my application to receive notification when the system is suspended or resumed.
MSDN documentation
Function I'd like to be executed after application receives notification (I tried both void and void CALLBACK and both work same way):
void isConnectedStandby()
{
printf( "ConnectedStandby Request");
}
1st case - I provide pointer to the isConnectedStandby function, but system treats as a double pointer to the function - it calls an address which is under this callback pointer.
HPOWERNOTIFY RegistrationHandle;
PowerRegisterSuspendResumeNotification(
DEVICE_NOTIFY_CALLBACK,
&isConnectedStandby,
&RegistrationHandle
);
2nd case - here I provide as follows (this way my function code is executed):
typedef void (*StatusFunction_t)();
StatusFunction_t StatusFunction = isConnectedStandby;
HPOWERNOTIFY RegistrationHandle;
PowerRegisterSuspendResumeNotification(
DEVICE_NOTIFY_CALLBACK,
&isConnectedStandby,
&RegistrationHandle
);
System calls not only mine function, but all addresses after the first one (if I provide an array of functions, it executes one after another to crash when there is no valid code available)
What is the correct way to use this function?
Function declaration (must be static ULONG with 3 parameters as you can see below):
static ULONG isConnectedStandby(PVOID Context, ULONG Type, PVOID Setting);
ULONG isConnectedStandby(PVOID Context, ULONG Type, PVOID Setting)
{
printf( "ConnectedStandby Request");
return 0;
}
Istead of providing callback function directly to PowerRegisterSuspendResumeNotification we have to provide struct _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS filled with our functions address :
static _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS testCallback = {
isConnectedStandby,
nullptr
};
HPOWERNOTIFY RegistrationHandle;
PowerRegisterSuspendResumeNotification(
DEVICE_NOTIFY_CALLBACK,
&testCallback,
&RegistrationHandle
);
MSDN documentation did not mention any of those information.