See also section 2.5.5.
The general philosophy for C++ classes is that a "half-created" object should not be possible - that is, the constructor should either succeed with a completely usable object or it should throw an exception. This API tries to follow that philosophy, but there are some important exceptions and caveats. (For more on how the C++ and Prolog exceptions interrelate, see section 2.18.)
The various classes (PlAtom
, PlTerm
, etc.)
are thin wrappers around the C interface's types (atom_t
,
term_t
, etc.). As such, they inherit the concept of "null"
from these types (which is abstracted as PlAtom::null
,
PlTerm::null
, etc., which typically is equivalent to
0
). Normally, you shouldn't need to check whether the
object is "fully created", but if you do, you can use the methods
is_null() or not_null().
Most of the classes have constructors that create a "complete" object. For example,
PlAtom foo("foo");
will ensure that the object foo
is useable and will
throw an exception if the atom can't be created. However, if you choose
to create an PlAtom
object from a atom_t
value, no checking is done (similarly, no checking is done if you create
a PlTerm
object from a term_t
value).
To help avoid programming errors, some of the classes do not have a
default "empty" constructor. For example, if you with to create a
PlAtom
that is uninitialized, you must explicitly use
PlAtom(PlAtom::null)
. This make some code a bit more
cumbersome because you can't omit the default constructors in struct
initalizers.
Many of the classes have a as_string() method - this might be changed
in future to to_string(), to be consistent with
std::to_string()
. However, the method names such as
as_int32_t() were chosen itnstead of to_int32_t() because they imply
that the representation is already an int32_t
, and not that
the value is converted to a int32_t
. That is, if the value
is a float, int32_t
will fail with an error rather than
(for example) truncating the floating point value to fit into a 32-bit
integer.
Many of the classes wrap long-lived items, such as atoms, functors,
predicates, or modules. For these, it's often a good idea to define them
as static
variables that get created at load time, so that
a lookup for each use isn't needed (atoms are unique, so
PlAtom("foo")
requires a lookup for an atom foo
and creates one if it isn't found).
C code sometimes creates objects "lazily" on first use:
void my_function(...) { static atom_t ATOM_foo = 0; ... if ( ! foo ) foo = PL_new_atom("foo"); ... }
For C++, this can be done in a simpler way, because C++ will call a
local “static
” constructor on first use.
void my_function(...) { static PlAtom ATOM_foo("foo"); }
The class PlTerm
(which wraps term_t
) is
the most used. Although a PlTerm
object can be created from
a term_t
value, it is intended to be used with a
constructor that gives it an initial value. The default constructor
calls PL_new_term_ref() and throws an exception if this fails.
The various constructors are described in
section 2.10.1. Note that the
default constructor is not public; to create a "variable" term, you
should use the subclass constructor PlTerm_var().