Re: FLI question
softinfo <softinfo@stamov.ch> writes:
> Hello,
>
> How can I call a C function from LispWorks Enterprise, which is not
> exported (for example in a DLL under Windows), but accessible only by
> a function pointer in structure which I receive from another function
> (which is accesible from FLI)?
>
>
>
> Example :
>
> the function (accessible by FLI) gives me a structure in the form
>
> struct {
> ...
> long (__stdcall *Fun) (int arg);
> ...
> }
>
> The function I would like to call is "Fun".
You catched me. I spend way too much time with it and what I have come
up with is an ugly hack. Well yes I check the docs, but I did not
found something about that all examples just work with arrays and
probably some values. There is one advanced example, which does not
cover this questio too. I was sure it would work, and in fact I got it
running, but as said it a hack...
This is my C-test file
struct foo {
double (*fun)(int);
};
double (*my_fun) (int);
double fahrenheit_to_celsius(int);
void assign_to_my_fun (double (*some_fun) (int)){
my_fun = some_fun;
}
double call_my_fun(int val){
return (my_fun(val));
}
double fahrenheit_to_celsius (int fahr){
return ((5.0 / 9.0) * (fahr - 32.0));
}
double other_fun(int val){
return (double) val;
}
Well you see I had to write some extra C code too. I introduced an
variable of the needed type double (*my_fun)(int) a setter for that
function and a functios which just does call the variable.
Than I let LispWorks write the wrappers:
M-x Evaluate Buffer
#| DATE : 27Sep2
| USER : frido
| PROCESSED FILE : /home/frido/programming/CL/fli-test/mylib.c
|#
(in-package "COMMON-LISP-USER")
;;; Derived from file : "/var/tmp/filelkIrYk.c"
(fli:define-c-struct (foo (:foreign-name "foo"))
(fun (:pointer (:function (:int) :double))))
(fli:define-foreign-variable (my-fun "my_fun" :source)
:type
(:pointer (:function (:int) :double)))
(fli:define-foreign-function (fahrenheit-to-celsius "fahrenheit_to_celsius"
:source)
((arg-1 :int))
:result-type
:double
:language
:c)
(fli:define-foreign-function (assign-to-my-fun "assign_to_my_fun"
:source)
((some-fun
(:pointer (:function (:int) :double))))
:result-type
:void
:language
:c)
(fli:define-foreign-function (call-my-fun "call_my_fun" :source)
((arg-1 :int))
:result-type
:double
:language
:c)
(fli:define-foreign-function (other-fun "other_fun" :source)
((val :int))
:result-type
:double
:language
:c)
This was easy wasn't it? This alone makes interfacing quite
easy... Well anyway we have to dig deeper.
How do we get it going than?
Let's see if we are on the right track.
But immediatly thereafter I realised that I did not know how to make
an external pointer from a Lisp Function which itself wraps a C
function. Ah well this round trips are really easy on your mind ;-)
Well apropos is your friend I hoped and typed in:
CL-USER 39 : 2 > (apropos "function-pointer")
FLI::%FUNCTION-POINTER
FLI::FUNCTION-POINTER-NAME
X-UTILITIES::MAKE-FOREIGN-FUNCTION-POINTER -- #<function X-UTILITIES::MAKE-FOREIGN-FUNCTION-POINTER 20381FBA>
X-UTILITIES::TRANSLATE-FROM-FUNCTION-POINTER -- #<function X-UTILITIES::TRANSLATE-FROM-FUNCTION-POINTER 203822C2>
X-UTILITIES::TRANSLATE-TO-FUNCTION-POINTER -- #<function X-UTILITIES::TRANSLATE-TO-FUNCTION-POINTER 203821C2>
XT-LIBRARY::FUNCTION-POINTER
:FUNCTION-POINTER-NAME -- value: :FUNCTION-POINTER-NAME
the third one looks as if it could be usefule. I therfor used it
Ctrl-Shift a has given me:
The lambda list of X-UTILITIES::MAKE-FOREIGN-FUNCTION-POINTER is
(FUNCTION-NAME FUNCTION-TYPE)
Well I guess you won't find documentation for internal functions, you
just have to guess what things might do. Ok for knowing how the later
might look I checked the docs for FLI and wrote this:
(setf cfun (X-UTILITIES::MAKE-FOREIGN-FUNCTION-POINTER
'fahrenheit-to-celsius '(:pointer (:function (:int) :double))))
#<Pointer to type (:FUNCTION (:INT)) = #x4015E8A8>
Uff, seems to be the right stuff ;-)
Now we have to assign to my_fun
(assign-to-my-fun cfun)
Now we call it through the wrappar function around my-fun
(call-my-fun 10)
-12.222222222222223
Well at least it sort-of works. Now we go back to you problem. The
thing with calling a function from a struct.
At first we have to assign a function to the function in the struct:
(we take the other function this time because we want to know if that
what I've done will really work kind-of ...)
First allocate space for the Structure
(setf c-foostruct (fli:allocate-foreign-object :type 'foo))
#<Pointer to type (:STRUCT FOO) = #x0823EFB8>
Ok, now give the fun structure element a function:
(setf (fli:foreign-slot-value c-foostruct 'fun)
(x-utilities::make-foreign-function-pointer'other-fun '(:pointer
(:function (:int) :double))))
#<Pointer to type (:FUNCTION (:INT)) = #x4015E8D8>
Well well heavy stuff. Now assign the function to my_fun:
CL-USER 59 : 1 > (assign-to-my-fun (fli:foreign-slot-value c-foostruct 'fun))
Ok now call it:
(call-my-fun 10)
10.0
Q.E.D.
I'm quite sure that there might be an easier way, because CAPI musst
Deal with functions in structs, but I'm sorry I have no better idea on
how to achieve what to do. Let's come back to your case.
You did not want to write wrappers. Well I can't help, it seems to
me as if you have to. It's not terrible difficult, but it's quite some
work.
The problem is you must have some way to assign functions to other
functions. This is ugly you might get a away with just a handful of
functions and maybe you can do it with void* pointers, well not all to
funny I guess.
Anyway I would offer to work on that stuff, if you bear the costs.
Regards
Friedrich