1.6 Overview
All Application Manual Name SummaryHelp

  • Documentation
    • Reference manual
    • Packages
      • A C++ interface to SWI-Prolog
        • A C++ interface to SWI-Prolog
          • Overview
            • Design philosophy of the classes
            • Summary of files
            • Summary of classes
            • Wrapper functions
            • Naming conventions, utility functions and methods
            • PlTerm class
            • PlTermScoped class (experimental)
            • Blobs
            • Limitations of the interface
            • Linking embedded applications using swipl-ld

1.6.7 PlTermScoped class (experimental)

This class is experimental and subject to change.

Normally all term references in a scope are discarded together or all term references created after a specific one are reclaimed using PlTerm::reset_term_refs(). A PlTermScoped object is the same as a PlTerm object except that PL_free_term_ref() is called on its wrapped term when the object goes out of scope. This shrinks the current foreign frame if the term is the last one in the frame and otherwise it marks it for reuse.

Here is an example, where PlTermScoped is inside a for-loop. If PlTerm were used instead, the stack would grow by the number of items in the array; PlTermScoped ensures that stack doesn't grow.10Assuming that unify_atom_list() is called from a predicate implementation, if PlTerm were used instead of PlTermCopy, all the created terms would be discarded when the Prolog stack frame is unwound; the use of PlTermScoped reuses the terms in that stack frame. A slightly more effiicient way of preventing the Prolog stack from growing is to use PlTerm::put_term() to reuse a term reference; but that is more difficult to understand and also more error-prone.

bool
unify_atom_list(const std::vector<std::string>& array, PlTerm list)
{ PlTermScoped tail(list); // calls PL_copy_term_ref() to copy `list`
  for( auto item : array )
  { PlTermScoped head; // var term
    PlCheckFail(tail.unify_list(head, tail));
    PlCheckFail(head.unify_chars(PL_ATOM, item));
  }
  return tail.unify_nil();
}

The design of PlTermScoped is modeled on std::unique_ptr11unique_ptr was originally called scoped_ptr in the Boost libraries, but the name was changed to contrast with std::shared_ptr, which is reference-counted. and uses move semantics to ensure safety.12Move semantics are a relatively new feature in C++ and can be a bit difficult to understand. Roughly speaking, a move is a copies the object and then calls its destructor, so that any further use of the object is an error. If an object defines move methods or constructors, it can optimize this operation, and also can catch certain kinds of errors at compile time.

A PlTermScoped object can be created either with or without a wrapped term - the PlTermScoped::reset() method sets (or nulls) the wrapped term. A PlTermScoped object cannot be copied or passed as a value to a function; the PlTermScoped::release() method returns the wrapped term and resets the PlTermScoped object so that any further use of the PlTermScoped object is an error.

As shown in the example above, PlTermScoped can be used instead of PlTerm, in places where a loop would otherwise cause the stack to grow. There are limitations on the operations that are allowed on a PlTermScoped object; in particular, a PlTermScoped object cannot be copied and cannot be implicitly converted to a Plterm.

The PlTermScoped constructors always create a new term ref, by calling either PL_new_term_ref() or PL_copy_term_ref(). If you try to copy or create a PlTermScoped object from another PlTermScoped object, you will get a compile-time error; you can set the value from a PlTerm object, which can be obtained by calling PlTermScoped::release().

The methods derived from the PL_put_*() and PL_cons_*() functions should not be used with a PlTermScoped object. If you need to use these, you can use PlTermScoped::get() to get a PlTerm, for which a put_*() method can be used.

To copy a PlTermScoped object or to pass it as a value in a function call, use the PlTermScoped::release() method or std::move():

  PlTermScoped ts(...);
  PlTerm t;

  // Copy to a PlTerm:
  t = ts.release(); // or: t = std::move(ts);

  // Pass as a value to a function:
  foo(ts.release()); // or: foo(std::move(ts);

  // Copy to a PlTermScoped:
  PlTermScoped ts2;
  ts2.reset(ts.release()); // or: ts2.reset(std::move(ts));

The methods are (in addition to, or overriding the methods in PlTerm):

PlTermScoped :: PlTermScoped()
- same as PlTermScoped(PlTerm::null).
PlTermScoped :: PlTermScoped(PlTerm t)
- set the value from t.copy_term_ref()
PlTermScoped :: PlTermScoped(term_t t)
- same as PlTermScoped(PlTerm(t)).
PlTermScoped :: PlTermScoped(PlTermScoped&& m)
- create a new wrapped object from m and reset m. This is typically used with std::move().
PlTermScoped& PlTermScoped::operator=(PlTermScoped&& m)
- copy m and reset it. This is typically used with std::move().
~ PlTermScoped()
- if the wrapped term not null, call PL_free_term_ref() on it.
PlTermScoped :: PlTermScoped(PlTermScoped& m)
- deleted method.
PlTermScoped& operator=(PlTermScoped& m)
- deleted method.
void PlTermScoped::reset()
- same as PlTermScoped::reset(PlTerm::null).
void PlTermScoped::reset(PlTerm src)
- sets the wrapped term from src. To set the wrapped term from a PlTermScoped, use PlTermScoped::release() to convert it to a PlTerm.
PlTerm PlTermScoped::get()
- convert the object to a PlTerm. This is typically used when calling a function that expects a PlTerm object and which will not call PlTerm::free_term_ref() on it.
PlTerm PlTermScoped::release()
- typically used in the context t2.reset(t.release()) to copy a PlTermScoped; this can also be written t2=std::move(t).
void PlTermScoped::swap(PlTermScoped& src)
- swap two PlTermScoped objects’wrapped terms.