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_ptr
11unique_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
, use PlTermScoped::release()
to convert it to a PlTerm
.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.t2.reset(t.release())
to copy a
PlTermScoped
; this can also be written
t2=std::move(t)
.PlTermScoped
objects’wrapped terms.