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, 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.