See also section 2.5.1.
The classes all have names starting with "Pl", using CamelCase; this contrasts with the C functions that start with "PL_" and use underscores.
The wrapper classes (PlFunctor
, PlAtom
,
PlTerm
), etc. all contain a field C_
that
contains the wrapped value (functor_t
, atom_t
, term_t
respectively). If this wrapped value is needed, it should be accessed
using the unwrap() or unwrap_ptr() methods.
In some cases, it's natural to use a pointer to a wrapper class. For
those, the function PlUnwrapAsPtr() returns nullptr
if the
pointer is null; otherwise it returns the wrapped value (which itself
might be some kind of "null").
The wrapper classes (which subclass WrappedC<...>
)
all define the following methods and constants:
null
).PlAtom
,
the constructor takes an atom_t
value).C_
- the wrapped value. This can be used directly when
calling C functions, for example, if t
and a
are of type PlTerm
and PlAtom
: Plcheck_PL(PL_put_atom(t.unwrap(),a.unwrap()))
.null
- the null value (typically 0
, but
code should not rely on this).is_null()
, not_null()
- test for the
wrapped value being null
.reset()
- set the wrapped value to null
reset(new_value)
- set the wrapped value from the
wrapped type (e.g., PlTerm:;reset(term_t new_value))reset_wrapped(new_value)
- set the wrapped value from
the same type (e.g., PlTerm::reset_wrapped(PlTerm new_value))bool
operator is disabled - you should use
not_null() instead.9The reason: a bool
conversion causes ambiguity with PlAtom(PlTterm)
and PlAtom(atom_t)
.
The method unwrap() can be used to access the C_
field,
and can be used wherever a atom_t
or term_t
is
used. For example, the PL_scan_options() example code can be
written as follows. Note the use of &callback.unwrap()
to pass a pointer to the wrapped term_t
value.
PREDICATE(mypred, 2) { auto options = A2; int quoted = false; size_t length = 10; PlTerm_var callback; PlCheckFail(PL_scan_options(options, 0, "mypred_options", mypred_options, "ed, &length, &callback.unwrap())); callback.record(); // Needed if callback is put in a blob that Prolog doesn't know about. // If it were an atom (OPT_ATOM): register_ref(). <implement mypred> }
For functions in SWI-Prolog.h
that don't have a C++
equivalent in SWI-cpp2.h
, PlCheckFail() is a convenience
function that checks the return code and throws a PlFail
exception on failure or PlException
if there was an
exception. The PREDICATE() code catches PlFail
exceptions
and converts them to the foreign_t
return code for failure.
If the failure from the C function was due to an exception (e.g.,
unification failed because of an out-of-memory condition), the foreign
function caller will detect that situation and convert the failure to an
exception.
The "getter" methods for PlTerm
all throw an exception
if the term isn't of the expected Prolog type. The "getter" methods
typically start with "as", e.g. PlTerm::as_string(). There are also
other "getter" methods, such as PlTerm::get_float_ex() that wrap PL_*()
functions.
"Getters" for integers have an additionnal problem, in that C++
doesn't define the sizes of int
, long
, or
size_t
. It seems to be impossible to make an overloaded
method that works for all the various combinations of integer types on
all compilers, so there are specific methods for int64_t
,
uint64_t
, size_t
.
In some cases,it is possible to overload methods; for example, this
allows the following code without knowing the exact definition of
size_t
:
PREDICATE(p, 1) { size_t sz; A1.integer(&sz); ... }
It is strongly recommended that you enable conversion checking.
For example, with GNU C++, these options (possibly with -Werror
):
-Wconversion -Warith-conversion -Wsign-conversion
-Wfloat-conversion
.
There is an additional problem with characters - C promotes them to int
but C++ doesn't. In general, this shouldn't cause any problems, but care
must be used with the various getters for integers.