• Places
    • Home
    • Graphs
    • Prefixes
  • Admin
    • Users
    • Settings
    • Plugins
    • Statistics
  • CPACK
    • Home
    • List packs
    • Submit pack
  • Repository
    • Load local file
    • Load from HTTP
    • Load from library
    • Remove triples
    • Clear repository
  • Query
    • YASGUI SPARQL Editor
    • Simple Form
    • SWISH Prolog shell
  • Help
    • Documentation
    • Tutorial
    • Roadmap
    • HTTP Services
  • Login

1.13 The class PlQuery
All Application Manual Name SummaryHelp

  • Documentation
    • Reference manual
    • Packages
      • A C++ interface to SWI-Prolog
        • A C++ interface to SWI-Prolog
          • The class PlQuery
            • The class PlFrame - Unification and foreign frames

1.13.1 The class PlFrame - Unification and foreign frames

As documented with PL_unify(), if a unification call fails and control isn't made immediately to Prolog, any changes made by unification must be undone. The functions PL_open_foreign_frame(), PL_rewind_foreign_frame(), PL_discard_foreign_frame(), and PL_close_foreign_frame() are encapsulated in the class PlFrame, whose destructor calls PL_close_foreign_frame(). Using this, the example code with PL_unify() can be written:

PREDICATE(can_unify_ffi, 2)
{ fid_t fid = PL_open_foreign_frame();

  int rval = PL_unify(A1.unwrap(), A2.unwrap());
  PL_discard_foreign_frame(fid);
  return rval;
}
    /* equivalent to the Prolog code
       T1 = T2 -> do_one_thing ; do_another_thing */
    { PlFrame fr;
      bool t1_t2_unified = A1.unify_term(A2);
      if ( ! t1_t2_unified )
        fr.rewind();
    if ( t1_t2_unified )
      do_one_thing(...);
    else
      do_another_thing(...);
}

The following is C++ version of the code example for PL_open_foreign_frame(). The calls to PL_close_foreign_frame() and the check for PL_exception(0) in the C code aren't needed in the C++ code:

static std::vector<std::string> lookup_unifies =
  { "item(one, 1)", "item(two, 2)", "item(three, 3)" };

PREDICATE(lookup_unify, 1)
{ PlFrame fr;
  for (auto& s : lookup_unifies )
  { PlCompound t(s);
    if ( A1.unify_term(t) )
      return true;
    fr.rewind();
  }
  return false;
}

or using this convenience wrapper:

    if ( RewindOnFail([t1=A1,t2=A2]()->bool
                      { return t1.unify_term(t2); }) )
      do_one_thing(...);
    else
      do_another_thing(...);

Note that PlTerm::unify_term() checks for an error and throws an exception to Prolog; if you wish to handle exceptions, you must call PL_unify_term(t1. unwrap(),t2. unwrap()).

The class PlFrame provides an interface to discard unused term-references as well as rewinding unifications (data-backtracking).

Reclaiming unused term-references is automatically performed after a call to a C++-defined predicate has finished and returns control to Prolog. In this scenario PlFrame is rarely of any use. This class comes into play if the toplevel program is defined in C++ and calls Prolog multiple times. Setting up arguments to a query requires term-references and using PlFrame is the only way to reclaim them.

Another use of of PlFrame is when multiple separate unifications are done - if any of them fails, then the earlier unifications must be undone before returning to Prolog.

PlFrame :: PlFrame()
Creating an instance of this class marks all term-references created afterwards to be valid only in the scope of this instance.
~ PlFrame()
Reclaims all term-references created after constructing the instance. If either close() or discard() have been called, the destructor does nothing.
void PlFrame::rewind()
Discards all term-references and global-stack data created as well as undoing all unifications after the instance was created.
void PlFrame::close()
Reclaims all term-references created after constructing the instance.
void PlFrame::discard()
Same as PlFrame::rewind() + PlFrame::close().
bool PlRewindOnFail(std::function<bool()> f)
is a convenience function that does a frame rewind if a function call fails (typically, failure due to unification failure). It takes a std::function<bool>()> as an argument, which is called in the context of a new PlFrame.

A typical use for PlFrame is the definition of C++ functions that call Prolog and may be called repeatedly from C++. Consider the definition of assertWord(), adding a fact to word/1; the PlFrame removes the new term av[0] from the stack, which prevents the stack from growing each time assertWord() is called:

void
assertWord(const char *word)
{ PlFrame fr;
  PlTermv av(1);

  av[0] = PlCompound("word", PlTermv(word));
  PlQuery q("assert", av);
  PlCheckFail(q.next_solution());
}

The following example uses PlFrame in the context of a foreign predicate. The can_unify/2’s truth-value is the same as for Prolog unification (=/2), but has no side effects. In Prolog one would use double negation to achieve this:

PREDICATE(can_unify, 2)
{ PlFrame fr;

  int rval = (A1=A2);
  fr.discard(); // or, less efficiently: fr.rewindd();
  return rval;
}

Here is an example of using PlRewindOnFail(), where name_to_terms contains a map from names to terms (which are made global by using the PL_record() function). The frame rewind is needed in the situation where the first unify_term() succeeds and the second one fails.

static const std::map<const std::string, PlRecord> name_to_term =
    { {"a", PlTerm(...).record(), PlTerm(...).record()},
    ... };

PREDICATE(name_to_terms, 3)
{ A1.must_be_atom_or_string();
  const auto it = name_to_term.find(A1.as_string());
  return it != name_to_term.cend() &&
    PlRewindOnFail([t1=A2,t2=A3,&it]()
                   { return t1.unify_term(it->second.first.term()) &&
                            t2.unify_term(it->second.second.term()); });
}

The equivalent code without using PlRewindOnFail() is:

PREDICATE(name_to_terms, 3)
{ PlTerm key(A1), term1(A2), term2(A3);
  const auto it = name_to_term.find(key.as_string());
  if ( it == name_to_term.cend() )
    return false;
  if ( !term1.unify_term(it->second.first.term()) )
    return false;
  PlFrame fr;
  if ( !term2.unify_term(it->second.second.term()) )
  { fr.discard();
    return false;
  }
  return true;
}

ClioPatria (version V3.1.1-51-ga0b30a5)