9.4 | Calling Prolog from C

- Introduction
- Example: `my_call/1` - a `call/1` clone
- Example: recovering the list of all operators

9.4.1 | Introduction

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

      void   Pl_Query_Begin        (Bool recoverable)
      int    Pl_Query_Call         (int functor, int arity, PlTerm *arg)
      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:

- open a query using `Pl_Query_Begin()`
- compute the first solution using `Pl_Query_Call()`
- eventually compute next solutions using `Pl_Query_Next_Solution()`
- close the query using `Pl_Query_End()`

The function `Pl_Query_Begin(recoverable)` is used to initialize a query. 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). All terms created in the heap, e.g. using `Mk_...` family functions (section 9.2.5), after the invocation of `Pl_Query_Begin()` can be recovered when calling `Pl_Query_End(TRUE)` (see below).
The function `Pl_Query_Call(functor, arity, arg)` calls a predicate passing arguments. It is then used to compute the first solution. The arguments `functor`, `arity` and `arg` are similar to those of the functions handling complex terms (section 9.2.1). This function returns:

- `PL_FAILURE` (a constant equal to `FALSE`, i.e. 0) if the query fails.
- `PL_SUCCESS` (a constant equal to `TRUE`, i.e. 1) in case of success. In that case the argument array `arg` can be used to obtain the unification performed by the query.
- `PL_EXCEPTION` (a constant equal to 2). In that case function `Pl_Get_Exception()` can be used to obtained the exceptional term raised by `throw/1` (section 6.2.4).

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_Call()` (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:

- `PL_RECOVER`: to recover the memory space consumed by the query. After that the state of Prolog stacks is exactly the same as before opening the query. To use this option the query must have been initialized specifying `TRUE` for `recoverable` (see above).
- `PL_CUT`: to cut remaining alternatives. The effect of this option is similar to a cut after the query.
- `PL_KEEP_FOR_PROLOG`: to keep the alternatives for Prolog. This is useful when the query was invoked in a foreign C function. In that case, when the predicate corresponding to the C foreign function is invoked a query is executed and the remaining alternatives are then available as alternatives of that predicate.

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 9.2.1).

9.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 `examp.pl`:

  `:- foreign(my_call(term)).`

In the C file `examp_c.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);
        Pl_Query_Begin(FALSE);
        result = Pl_Query_Call(functor, arity, arg);
        Pl_Query_End(PL_KEEP_FOR_PROLOG);
        return (result == PL_SUCCESS);
      }

The compilation produces an executable called `examp`:

  `% gplc examp.pl examp_c.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 *args;
        int functor, arity;
        int result;

        args = Rd_Callable_Check(goal, &functor, &arity);
        Pl_Query_Begin(FALSE);
        result = Pl_Query_Call(functor, arity, args);
        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;
      }

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;
      }

9.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 `examp.pl`:

  `:- foreign(all_op(term)).`

In the C file `examp_c.c`:

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

      Bool
      all_op(PlTerm list)
      {
        PlTerm op[1024];
        PlTerm args[3];
        int n = 0;
        int result;

        Pl_Query_Begin(TRUE);
        args[0] = Mk_Variable();
        args[1] = Mk_Variable();
        args[2] = Mk_Variable();
        result = Pl_Query_Call(Find_Atom("current_op"), 3, args);
        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_Call` and `Pl_Query_Next_Solution` can be considered as a boolean.
The compilation produces an executable called `examp`:

  `% gplc examp.pl examp_c.c`

Example of use:

      | ?- all_op(L).

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

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

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

Copyright (C) 1999-2002 Daniel Diaz
Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.
More about the copyright