The original version of the C++ interface heavily used implicit constructors and conversion operators. This allowed, for example:
PREDICATE(hello, 1) { cout << "Hello " << (char *)A1 << endl; // Deprecated return true; } PREDICATE(add, 3) { return A3 = (long)A1 + (long)A2; // Deprecated }
Version 2 is a bit more verbose:
PREDICATE(hello, 1) { cout << "Hello " << A1.as_string() << endl; return true; } PREDICATE(add, 3) { return A3.unify_int(A1.as_long() + A2.as_long()); }
There are a few reasons for this:
bool
. In addition, the result
of unification should always be checked (e.g., an “always succeed” unification
could fail due to an out-of-memory error); the unify_XXX()
methods return a bool
and they can be wrapped inside a PlCheckFail()
to raise an exception on unification failure.(char*)A1
becomes the more verbose
static_cast<std::string>(A1)
, which is longer than
A1.as_string()
.
Also, the string casts don't allow for specifying encoding.PlTerm t; Pl_put_atom_chars(t, "someName");
whereas this is now required:
PlTerm t; Pl_put_atom_chars(t.as_term_t(), "someName");
However, this is mostly avoided by methods and constructors that wrap the foreign language functions:
PlTerm_atom t("someName");
or
auto t = PlTerm_atom("someName");
Additionally, there are now wrappers for most of the PL_*() functions that check the error return and throw a C++ exception as appropriate.
Over time, it is expected that some of these restrictions will be eased, to allow a more compact coding style that was the intent of the original API. However, too much use of overloaded methods/constructors, implicit conversions and constructors can result in code that's difficult to understand, so a balance needs to be struck between compactness of code and understandability.
For backwards compatibility, much of the version 1 interface is still available (except for the implicit constructors and operators), but marked as “deprecated” ; code that depends on the parts that have been removed can be easily changed to use the new interface.