Next: , Previous: , Up: Calling Prolog from C   [Contents][Index]


6.5.2 Finding Multiple Solutions of a Call

If you are interested in more than one solution, then a more complicated scheme is used. You find the predicate definition as above, but you do not call the predicate directly.

  1. Set up a call with SP_open_query()
  2. Call SP_next_solution() to find a solution. Call this predicate again to find more solutions if there are any.
  3. Terminate the call with SP_close_query() or SP_cut_query()

The function SP_open_query() will return an identifier of type SP_qid that you use in successive calls. Note that if a new query is opened while another is already open, then the new query must be terminated before exploring the solutions of the old one. That is, queries must be strictly nested.

The function SP_next_solution() will cause the Prolog engine to backtrack over any current solution of an open query and look for a new one.

A query must be terminated in either of two ways. The function SP_cut_query() will discard the choices created since the corresponding SP_open_query(), like the goal !. The current solution is retained in the arguments until backtracking into any enclosing query.

Alternatively, the function SP_close_query() will discard the choices created since the corresponding SP_open_query(), and then backtrack into the query, throwing away any current solution, like the goal !, fail.

A simple way to call arbitrary Prolog code, whether for one solution or for multiple solutions, is to use SP_read_from_string() (see Creating Prolog Terms) to create an argument to call/1. It is a good idea to always explicitly specify the module context when using call/1 or other meta-predicates from C.

It is important to understand the rules governing the scope of SP_term_refs, and the terms they hold, in conjunction with calls from Prolog to C and vice versa. SP_term_refs are internally stored on a stack, which is manipulated by the various API functions as follows:

SP_new_term_ref()

The new SP_term_ref is pushed onto the stack.

calling C from Prolog
SP_query()
SP_query_cut_fail()

The top of the stack is saved on call and restored upon return.

SP_open_query()

The top of the stack is saved in the new query.

SP_close_query()
SP_cut_query()
SP_next_solution()

The top of the stack is restored from the query argument.

Among other things, this means that an SP_term_ref cannot be saved across multiple calls from Prolog to C. Thus it makes no sense to declare an SP_term_ref as a static C variable.

Prolog terms are also generally stored on a stack, which keeps growing until the execution backtracks, either spontaneously or by calling SP_close_query() or SP_next_solution(). It is an abuse of the SP_open_query() API to assign a term to an SP_term_ref, and then backtrack over the term while the SP_term_ref is still live. Such abuse results in a dangling pointer that can potentially crash SICStus Prolog. The API typically follows the pattern:

  ...
  SP_pred_ref pred = SP_predicate(...);
  SP_term_ref ref1 = SP_new_term_ref();
  SP_qid goal = SP_open_query(pred,ref1,...);
  /* 
   * PART A: perform some initializations, and
   * loop through all solutions.
   */
  while (SP_next_solution(goal)==SP_SUCCESS) {
    /* 
     * PART B: perform some action on the current solution.
     */
  }
  
  SP_close_query(goal);
  ...

In order to avoid dangling pointer hazards, we recommend some simple coding rules:

PART A

In this part of the code, do not call SP_new_term_ref() or the functions in Creating Prolog Terms at all.

PART B

In this part of the code, do not call SP_new_term_ref() except to initialize any SP_term_refs declared locally to Part B. Do Not call the functions in Creating Prolog Terms, except to set SP_term_refs declared locally to Part B.



Send feedback on this subject.