For Programmers: Free Programming Magazines  


Home > Archive > Prolog > June 2007 > Re: Possible to register a foreign function dynamically?









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author Re: Possible to register a foreign function dynamically?
yuce

2007-05-29, 10:03 pm


Returning 0 at atom_checksum doesn't help, I think I should get my
hands dirty and use a debugger as you said :) At least now I know
there's no problem with PL_register_foreign.

I've tried a few Python packages (for SWI-Prolog and GNU-Prolog) last
year and couldn't make any of them work, and I think my approach to
use SWI-Prolog in a shared library is easier for me and the users; if
I am not wrong I've managed to use PySWIP (last year) even without
installing SWI-Prolog, only having libpl.so, boot32.prc and setting
SWI_HOME_DIR, but I need to rediscover it :)

Anyway, thanks for your help, I'll post here again if I succeed in
getting atom_checksum work.

Yuce


On May 29, 5:44 pm, Jan Wielemaker <j...@nospam.ct.xs4all.nl> wrote:
> On 2007-05-29, yuce <yucete...@gmail.com> wrote:
>
>
>
>
>
>
>
> Thats because load_foreign_library/1 loads a DLL/so file, looks for
> install() in there and calls it. Anything else that can get hold of a
> pointer to function of the right signature can call PL_register_foreign
> without problems.
>
>
>
>
> You definitely have a problem returning False. Does that make the
> function actually return 0? Maybe you can run it under a debugger
> and get a clue?
>
> Success --- Jan
>
> P.s. There is already an interface called pyprolog. Rumours say
> that isn't very complete nor maintained. I have no clue.



Jan Wielemaker

2007-05-31, 8:04 am

On 2007-05-30, yuce <yucetekol@gmail.com> wrote:
> (My previous post didn't appear, sending it again)
>
>
> Yes! After some time I managed to get it right. This must be my lucky
> wednesday :) Anyway, here's the complete Python program:
>
> from pyswip import *
> from pyswip.util import Prolog
>
> def atom_checksum(a0, arity, context):
> s = c_char_p("\x00"*MAXSTR)
> if PL_get_atom_chars(a0, addressof(s)):


Hmmm. PL_get_atom_chars stores a pointer to a string over the
second argument. The "\x00"*MAXSTR looks a bit odd to me.

> sum = 0
> for c in s.value:
> sum += ord(c)&0xFF;
> return PL_unify_integer(a0 + 1, sum&0xFF)
> else:
> return 0
>
> funtype = CFUNCTYPE(foreign_t, term_t, c_int, c_void_p)
> p = Prolog()
> PL_register_foreign("atom_checksum", 2, funtype(atom_checksum),
> PL_FA_VARARGS)
> print list(p.query("X='Python', atom_checksum(X, Y)",
> catcherrors=True))
>
> Outputs: [{'Y': '130', 'X': 'Python'}]
>
> I've tried disabling signals but I got segmentation faults when the
> program wasn't working right; but now everything seems fine [I don't
> want to disable signals, do you think disabling them would be better?]


As long as Prolog signal handling isn't conflicting with the signal
handling of another component (i.e. python) there is no reason to
disable it.

--- Jan
Jan Wielemaker

2007-06-01, 4:20 am

On 2007-05-31, yuce <yucetekol@gmail.com> wrote:
>
>
> I thought I have to get the memory for s, but looking at the C-source,
> it seems PL_get_atom_chars allocates it; if you could confirm that,


PL_get_atom_chars() provides a pointer to the actual string that is
part of the Prolog atom. Don't change! It remains valid inside a
predicate definition due to the fact that it is an argument to an
active call and thus protected from the atom garbage collector. If
you want to keep it longer you must either use PL_register_atom() to
lock the atom from atom-gc or copy the string. In this example there
is of course no need to keep it longer.

Cheers --- Jan

> I'll replace that line with `s = c_char_p()`
> [In python, "\x00"*MAXSTR creates a string of length MAXSTR having
> "\x00" as each character. I used it just to create some space for the
> string]
>
> Thanks :)
>
> Yuce
>

Jan Wielemaker

2007-06-01, 8:05 am

On 2007-06-01, yuce <yucetekol@gmail.com> wrote:
> Ok, from what I understand, PL_get_atom_chars doesn't allocate
> anything, it just returns the pointer to the actual string, so I don't
> need to allocate any memory to keep it.


Yes. That is, unless you want the string to survive the lifetime of
the predicate-call, as atom-gc can come along anytime after completion
of the predicate and destroy the atom, invalidating the string.
Actually it can also come along during the lifetime of the predicate
if you create an atom or even concurrently if there are multiple
Prolog threads active. That however doesn't harm you as the atom is
accessible through the life arguments of the call and will therefore
not be garbage collected (hopefully :-).

> I am trying to make things more Python then C, so I am wrapping PL_*
> functions in similar Python functions [for example,
> `PL_register_foreign` becomes registerForeign]. Here's the result of
> applying that to atom_checksum:
>
> from pyswip.prolog import Prolog
> from pyswip.easy import getAtomChars, unifyInteger, registerForeign
>
> def atom_checksum(*a):
> s = getAtomChars(a[0])
> if s is not None:
> sum = 0
> for c in s:
> sum += ord(c)&0xFF
> return unifyInteger(a[1], sum&0xFF)
> else:
> return False
> atom_checksum.arity = 2
>
> p = Prolog()
> registerForeign(atom_checksum)
> print list(p.query("X='Python', atom_checksum(X, Y)"))
>
> My next aim is covering the SWI-Prolog FFI functions responsible for
> creating terms and predicates, so avoiding the need to consult a
> Prolog program for complex code. I think this could be more flexible
> [but maybe with a slightly less performance]
>
> If you would like to comment on any of these, I'd appreciate that :)


If above is how Python programmers would like to see it, I guess it is
fine. The divantage of moving further away from the C interface is
that you cannot easily translate example code written in C to Python.


Cheers --- Jan

P.s. Does this also allow easy embedding of Python in Prolog?
P.s. Make sure to avoid global variables to make the interface
thread-safe. The only thing global is the initialisation
of the Prolog system, as there can only be one of them.
Jan Wielemaker

2007-06-01, 10:07 pm

On 2007-06-01, yuce <yucetekol@gmail.com> wrote:
>
> The PL_* functions of PySWIP are still available to the programmers,
> so C sources will be easily transformed to Python; but I need to have
> another layer that makes the task easier since we [Python programmers]
> are a little lazy I guess :) For example, instead of the following to
> retrieve the items of a list:
>
> # t is of type term_t
> head = PL_new_term_ref()
> items = []
> v = c_char_p()
> while PL_get_list(t, head, t):
> PL_get_chars(head, addressof(v), CVT_ALL|CVT_WRITE)
> items.append(v.value)
>
> I wrapped that into getList, so it becomes:
>
> items = getList(t)
>
> I think this is a great improvement for usability. Also I am trying to
> follow the same naming convention, so PL_get_list becomes getList, or
> PL_get_atom_chars becomes getAtomChars; so I guess someone used the C
> API will be able to start programming with PySWIP immediately [or in a
> very short time].


Hmmm. Its not ideal to rename PL_get_list into getList (your systematic
translation) and give it a different meaning. Converting the list
elements to text is probably not always what is intended. I'd call this
something like listToStrings(). Also be aware that PL_get_chars()
doesn't always return pointers to static strings. It does for atoms. but
in other cases it uses a ring of 16 buffers (see BUF_RING in the
description), That is fine for simple conversion and processing of
arguments, but if you convert a list of more than 16 elements that are
not atoms you have a problem, unless "items.append(v.value)" makes a
copy.

>
> I haven't factored this yet, in fact I have no idea how it can be done
> [apart from register_foreign]


Apart from starting it works the same. It you embed Prolog you load the
Prolog virtual machine and call PL_initialise. If you embed Python in
Prolog you must load it through load_foreign_library() and start it.

>
> I haven't tested this on a multi-threaded program yet but I don't have
> any global variables so there shouldn't be any problems. I have a
> singleton Prolog class which initializes and finalizes the system,
> here's the code for initialization:
>
> def __initialize(cls):
> plargs = (c_char_p*3)()
> plargs[0] = "./"
> plargs[1] = "-q"
> plargs[2] = "\x00"
> PL_initialise(2, plargs)


plargs[2] should be a NULL pointer, not a pointer to a string holding a
0-byte according to the normal argc/argv conventions. It won't harm
though as far as I know.

> swipl_fid = PL_open_foreign_frame()
> swipl_load = PL_new_term_ref()
> PL_chars_to_term("asserta(pyrun(GoalString,BindingList):-
> (atom_chars(A,GoalString),"
> " atom_to_term(A,Goal,BindingList),call(Go
al))).",
> swipl_load)
>
> PL_call(swipl_load, None)
> PL_discard_foreign_frame(swipl_fid)
> __initialize = classmethod(__initialize)
>
> `pyrun` is used when querying SWI-Prolog [I haven't write this code
> myself, and I will remove the Prolog code once I add the predicate/
> term creation functions]


As you figured out the existence of PL_chars_to_term(), I guess you can
avoid the use of pyrun/2. As is, it is pretty slow. First send a list of
characters as a Prolog string, then create an atom (also expensive),
than call the Prolog parser on it and finally do a meta-call.

> The finalization code just calls `PL_halt(0)`.
>
> How it seems?


Moving in the right direction. Might be wise to take this away from
comp.lang.prolog though. Most people here are only interested in the
result I guess :-)

Cheers --- Jan

>
> Thanks again :)
>
> Yuce
>
>

Jgressier

2007-06-02, 7:05 pm

> Moving in the right direction. Might be wise to take this away from
> comp.lang.prolog though. Most people here are only interested in the
> result I guess :-)


No, please. I am closely following this.
It is most interesting.

I'm porting a large software to Linux. The Gui was written in VB (30% of
the code) and the rest in Visual Prolog and some Python code. I've
almost finished tranlating VB to FreePascal under Lazarus. I didn't have
to change a single line in the Visual Prolog code (a dll) but since the
developpers drop the linux version of their Prolog, I turned to
swi-prolog. I am now on the point of translating the whole visual Prolog
code into swi-prolog. I am very much interested in any technical
discussion about foreign interfaces.

Best regards
J.Gressier
Jan Wielemaker

2007-06-02, 7:05 pm

On 2007-06-02, Jgressier <"pasdespam j.gressier"@laposte.net> wrote:
>
> No, please. I am closely following this.
> It is most interesting.
>
> I'm porting a large software to Linux. The Gui was written in VB (30% of
> the code) and the rest in Visual Prolog and some Python code. I've
> almost finished tranlating VB to FreePascal under Lazarus. I didn't have
> to change a single line in the Visual Prolog code (a dll) but since the
> developpers drop the linux version of their Prolog, I turned to
> swi-prolog. I am now on the point of translating the whole visual Prolog
> code into swi-prolog. I am very much interested in any technical
> discussion about foreign interfaces.


Ok. Surely SWI-Prolog won't drop Linux :-) Isn't there a VB for Linux
these days as well?

Success --- Jan
Jgressier

2007-06-02, 7:05 pm



> Ok. Surely SWI-Prolog won't drop Linux :-) Isn't there a VB for Linux
> these days as well?
>
> Success --- Jan


Well yes, there is RealBasic which I superficially tested. Anyhow, I
prefer free languages on one hand and I was not looking for a new Basic
actually. I just needed a real RAD. I've been designing the GUI part of
my softwares with RAD tools (VB, Delphi) for years now and I find it
very difficult to change my habits. From where I stand, there's no
quicker way to port the software under Linux. I also know XPCE, I mean,
I just read the documentation 2 years ago (:-) ). It is intellectually
satisfying and I'm sure I'd enjoy programming with it but I 'd
undoubtedly be very slow in comparison.

Under Lazarus, I just drop on the form the same object (or its
equivalent) that I used under VB, then I copy the code attached to the
vb object and paste it to the Lazarus/FreePascal object. Of course
there's a bit of translation to perform but it's rather straightforward.

I used to call python code through COM objetcs and Visual Prolog through
a DLL. Passing data from VB to the DLL written in V.Prolog is direct
(for basic types) and retrieving data is quite simple thanks to 2
primitives; one allowing to get a pointer to a string and another
allowing to move a block of memory.

Here's the idea : both arguments are strings and input. The first one
has been allocated by vb (and assigned with blanks), the second has been
computed by Prolog.

copy_in_vb_string (VB_Str,PrologString) :-
% length of the prolog string
str_len(PrologString, PrologStrLen),
% gives the pointer of this prolog string
PrologString_Pointer = cast(POINTER, PrologString),
% gives the pointer of the vb string
VB_OutStr_Pointer = cast(POINTER, VB_Str),
% copy the prolog string into the vb one.
movmem(PrologString_Pointer, VB_OutStr_Pointer, PrologStrLen).

The programmer doesn't have to bother to allocate/Free memory, it's all
done by vb and Prolog since to call a prolog predicate :

vb code :
vbstring=space (1024) ' ie
getdummystring_vb (vbstring) ' calls prolog "predicate"
vbstring = trim(vbstring)
...........

Prolog code :

getdummystring_vb (VB_string) :-
% some prolog code here with PrologString unified with a string
copy_in_vb_string (VB_string,PrologString),
fail.
getdummystring_vb (VB_string).

It is not orthodoxy ; I hope that this insurrectionnal situation will
meet some benevolence.

Well, anyhow, thank you very much for your tremendous work on swi-prolog.


Best regards
J.Gressier



Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2008 codecomments.com