Without pasting *all* my code, I have wrapped dlopen/dlclose/dlsym and dlerror 
and then added this to my C code base:

typedef int (*FUNCPTR)(int,int);

PlBool
ffi_call2(PlLong fn, PlLong n1, PlLong n2, PlLong* result)
{
  FUNCPTR callee = (FUNCPTR)fn;
  *result = (*callee)( (int)n1, (int)n2 );
  return PL_TRUE;
}

and in my interface test file I have this:

:- foreign( dlopen( +codes, -positive ),           [ fct_name( gp_dlopen )]).
:- foreign( dlclose( +positive ),                  [ fct_name( gp_dlclose )]).
:- foreign( dlclose_strict( +positive ),           [ fct_name( 
gp_dlclose_strict )]).
:- foreign( dlerror( -string ),                    [ fct_name( gp_dlerror )]).
:- foreign( dlsym( +positive, +codes, -positive ), [ fct_name( gp_dlsym )]).

%% This is to test the "add2" function to establish the protocol...
:- foreign( ffi_call2( +positive, +positive, +positive, -positive )).

and finally, some actual prolog test code and the output…
test1(N1,N2,Result) :-
        ( dlopen("libtest.so", H)
        ->
          dlsym(H, "add2", F),
          test2(F,N1,N2,Result),
          dlclose(H)
        ;
          dlerror(M),
          format("ERROR: ~a~n", [M])
        ).
%% Proves I can pass "F" around and it still work mightily...
test2(F,N1,N2,Result) :-
    ffi_call2(F, N1, N2, Result).

and finally the output:
| ?- test1(100,333,X).
dlsym: handle:7fd1b2c19b10, symbol: add2 ==> 109349f50

X = 433

yes

Hurrah, it works. The extra output is a printf() in the C code just to show 
what's going on.

That's all fine and well but as it is right now I would have to create lots of 
"FUNCPTR" variations to cope with all the possible signature types… obviously 
unworkable as this is intended to be a dynamic system. The question then is 
what is the most effective way to allow call-site code to arbitrarily load a 
library, grab a function and then call it with prolog terms as arguments?

I can use FIOArg* as the input, I can use a list of them to cope with multiple 
functions and assume the last one is a return value. IFF there is a return 
value. Now you see the enormity of the task, how to generalise a call to any 
function that takes any number of differing types of arguments and may or may 
not return something on the stack or be "void".

I think the the ultimate answer is some filthy dirt raw assembler code that 
pushes stuff onto the stack and then does the call and tidies up the stack 
frame etc. I don't mind doing that but I was hoping that seeing as how 
"foreign" already exists and gplc does such an amazing job that there might be 
code "inside the box" that I can re-use for this. I would dearly love to create 
a generic FFI for GNU Prolog that we could then use to wrap and call any 
externally available C library.

Of course, the calling code will know the context of what it is trying to do 
and can supply the necessary information which led me to some dreamt up vapour 
code that might look like this:

    ffi_call( "lib test.so", int( add2( int(100), int(333))), Result).
    ffi_callV( "lib test.so", add2( int(100), int(333)))). %% callV => VOID, no 
return value

Inside my C code I could then pick apart the functor and its arguments and do 
the right thing…somehow. I smell assembler! I don't mind. I cut my teeth on it 
28 years ago and still program PIC micros for people so I am not afraid of that 
or the dark!

I am already having delusions of grandeur and thinking about using RabbitMQ, 
Redis and MySQL and writing non-deterministic code that returns each row of a 
result set or keeps reading from an AMQP queue just because I can. For my day 
job, the ability to showcase Prolog would be great but it has to connect to all 
the usual stuff to be seen to be useful.

Ha. Welcome to the world of FFI! OK, 11:56 PM in the UK again, time for tubby 
bye byes.

:)
Cheers,
Sean.

_______________________________________________
Users-prolog mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/users-prolog

Reply via email to