Previous Contents Next
8.4 Calling Prolog from C

8.4.1 Introduction
The following functions allows a C function to call a Prolog predicate:

int    Pl_Query_Start        (int functor, int arity, PlTerm *arg,
                              Bool recoverable)
int    Pl_Query_Next_Solution(void)
void   Pl_Query_End          (int op)
PlTerm Pl_Get_Exception      (void)
void   Pl_Exec_Continuation  (int functor, int arity, PlTerm *arg)
The invocation of a Prolog predicate should be done as follows:

The function Pl_Query_Start(functor, arity, arg, recoverable) is used to initialize a query (computes the first solution). The arguments functor, arity and arg are similar to those of the functions handling complex terms (section 8.2.1). The argument recoverable shall be set to TRUE if the user wants to recover, at the end of the query, the memory space consumed by the query (in that case an additional choice-point is created). This functions returns:

The function Pl_Query_Next_Solution() is used to compute a new solution. It must be only used if the result of the previous solution was PL_SUCCESS. This functions returns the same kind of values as Pl_Query_Start() (see above).

The function Pl_Query_End(op) is used to finish a query. This function mainly manages the remaining alternatives of the query. However, even if the query has no alternatives this function must be used to correctly finish the query. The value of op is:

Note that several queries can be nested since a stack of queries is maintained. For instance, it is possible to call a query and before terminating it to call another query. In that case the first execution of Pl_Query_End() will finish the second query (i.e. the inner) and the next execution of Pl_Query_End() will finish the first query.

Finally, the function Pl_Exec_Continuation(functor, arity, arg) replaces the current calculus by the execution of the specified predicate. The arguments functor, arity and arg are similar to those of the functions handling complex terms (section 8.2.1).

8.4.2 Example: my_call/1 - a call/1 clone

We here define a predicate my_call(Goal) which acts like call(Goal) except that we do not handle exceptions (if an exception occurs the goal simply fails):

In the prolog file prog.pl:

:- foreign(my_call(term)).
In the C file utils.c:

#include <string.h>
#include "gprolog.h"

Bool my_call(PlTerm goal)

{
 PlTerm *arg;
 int     functor, arity;
 int     result;

 arg=Rd_Callable_Check(goal, &functor, &arity);
 result=Pl_Query_Start(functor, arity, arg, FALSE);
 Pl_Query_End(PL_KEEP_FOR_PROLOG);
 return (result==PL_SUCCESS);
}
The compilation produces an executable called prog:

% gplc prog.pl utils.c
Examples of use:

| ?- my_call(write(hello)).
hello
 
| ?- my_call(for(X,1,3)).
 
X = 1 ?    (here the user presses ; to compute another solution)
 
X = 2 ?    (here the user presses ; to compute another solution)
 
X = 3    (here the user is not prompted since there is no more alternative)
 
| ?- my_call(1).
{exception: error(type_error(callable,1),my_call/1)}
 
| ?- my_call(call(1)).
 
no
When my_call(1) is called an error is raised due to the use of Rd_Callable_Check(). However the error raised by my_call(call(1)) is ignored and FALSE (i.e. a failure) is returned by the foreign function.

To really simulate the behavior of call/1 when an exception is recovered it should be re-raised to be captured by an earlier handler. The idea is then to execute a throw/1 as the continuation. This is what it is done by the following code:

#include <string.h>
#include "gprolog.h"

Bool my_call(PlTerm goal)

{
 PlTerm *arg;
 int     functor, arity;
 int     result;

 arg=Rd_Callable_Check(goal, &functor, &arity);
 result=Pl_Query_Start(functor, arity, arg, FALSE);
 Pl_Query_End(PL_KEEP_FOR_PROLOG);
 if (result==PL_EXCEPTION)
    {
     PlTerm except=Pl_Get_Exception();
     Pl_Exec_Continuation(Find_Atom("throw"),1,&except);
    }
 return (result==PL_SUCCESS);
}
The following code propagates the error raised by call/1.

| ?- my_call(call(1)).
{exception: error(type_error(callable,1),my_call/1)}
Finally note that a simpler way to define my_call/1 is to use Pl_Exec_Continuation() as follows:

#include <string.h>
#include "gprolog.h"

Bool my_call(PlTerm goal)

{
 PlTerm *args;
 int    functor,arity;

 args=Rd_Callable_Check(goal, &functor, &arity);
 Pl_Exec_Continuation(functor, arity, args);
 return TRUE;
}
8.4.3 Example: recovering the list of all operators

We here define a predicate all_op(List) which unifies List with the list of all currently defined operators as would be done by: findall(X,current_op(_,_,X),List).

In the prolog file prog.pl:

:- foreign(all_op(term)).
In the C file utils.c:

#include <string.h>
#include "gprolog.h"

Bool all_op(PlTerm list)

{
 PlTerm op[1024];
 PlTerm args[3];
 int    n=0;
 int    result;

 args[0]=Mk_Variable();
 args[1]=Mk_Variable();
 args[2]=Mk_Variable();
 result=Pl_Query_Start(Find_Atom("current_op"), 3, args, TRUE);
 while(result)
    {
     op[n++]=Mk_Atom(Rd_Atom(args[2])); /* arg #2 is the name of the op */
     result=Pl_Query_Next_Solution();
    }
 Pl_Query_End(PL_RECOVER);

 return Un_Proper_List_Check(n, op, list);
}
Note that we know here that there is no source for exception. In that case the result of Pl_Query_Start and Pl_Query_End can be considered as a boolean.

The compilation produces an executable called prog:

% gplc prog.pl utils.c
Example of use:

| ?- all_op(L).

L = [:-,:-,\=,=:=,#>=,#<#,@>=,-->,mod,#>=#,**,*,+,+,',',...]

| ?- findall(X,current_op(_,_,X),L).

L = [:-,:-,\=,=:=,#>=,#<#,@>=,-->,mod,#>=#,**,*,+,+,',',...]

Copyright (C) 1999,2000 Daniel Diaz

Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.

More about the copyright
Previous Contents Next