2.5.7 Limitations of the interface
The C++ API remains a work in progress.
2.5.7.1 Strings
SWI-Prolog string handling has evolved over time. The functions that
create atoms or strings using char* or wchar_t*
are "old school"; similarly with functions that get the string as
char* or wchar_t*. The PL_get_unify_put_[nw]chars()
family is more friendly when it comes to different input, output,
encoding and exception handling.
Roughly, the modern API is PL_get_nchars(), PL_unify_chars() and PL_put_chars() on terms. There is only half of the API for atoms as PL_new_atom_mbchars() and PL-atom_mbchars(), which take an encoding, length and char*.
For return values, char* is dangerous because it can
point to local or stack memory. For this reason, wherever possible, the
C++ API returns a std::string, which contains a copy of the
the string. This can be slightly less efficient that returning a
char*, but it avoids some subtle and pervasive bugs that
even address sanitizers can't detect.16If
we wish to minimize the overhead of passing strings, this can be done by
passing in a pointer to a string rather than returning a string value;
but this is more cumbersome and modern compilers can often optimize the
code to avoid copying the return value.
2.5.7.2 Object handles
Many of the "opaque object handles", such as atom_t,
term_t, and functor_t are integers.17Typically uintptr_t
values, which the C standard defines as “an unsigned integer type
with the property that any valid pointer to void can be converted to
this type, then converted back to pointer to void, and the result will
compare equal to the original pointer.'' As such, there is
no compile-time detection of passing the wrong handle to a function.
This leads to a problem with classes such as PlTerm -
C++ overloading cannot be used to distinguish, for example, creating a
term from an atom versus creating a term from an integer. There are a
number of possible solutions, including:
- A subclass for each kind of initializer;
- A tag for each kind of intializer;
- Change the the C code to use a
structinstead of an integer.
It is impractical to change the C code, both because of the amount of edits that would be required and also because of the possibility that the changes would inhibit some optimizations.
There isn't much difference between subclasses versus tags; but as a matter of design, it's better to specify things as constants than as (theoretically) variables, so the decision was to use subclasses.