Subject: Re: [SWIPL] no copy semantics for PlTerm, in the C++ interface

From: Jan Wielemaker <[email-masked]>
Date: Tue, 6 Nov 2001 15:56:24 +0100

On Mon, 05 Nov 2001, Volker Wysk wrote:
>On Mon, 5 Nov 2001, Jan Wielemaker wrote:
>
>> >I'm a little confused now. As far as I can tell from the SWI-Prolog
>> >documentation, we have
>> >- Terms inside the Prolog machine
>> >- pointers inside Prolog to terms (= variable bindings?)
>> >- corresponding term_t values on the C side
>> >- PlTerm objects
>>
>> term_t is the same as PlTerm.  term_t is an indirect reference.  It is
>> the index of a cell on the local stack containing the value (or a
>> reference to the value, but the C/C++ user doesn't need to be aware
>> of this.
>>
>> If you use plain copy of the term_t value, both refer to the same
>> value-cell.  And thus:
>>
>>    <x bound to foo(1)>
>>    term_t y = x;
>>    PL_put_atom_chars(y, "hello");
>>    <both x and y refer to the atom 'hello'>
>>
>> If you want to see PlTerm objects as holders for a Prolog term writing
>> to the copy should not affect the original.  SWI-Prolog defines
>> PL_copy_term_ref(), which createa a new term-reference which points to
>> the same Prolog term.  Further unification inside this term affects
>> both, but assignments using PL_put_* affects only the addressed
>> reference.
>
>So the cells on the local stack are references to terms, and PlTerm
>objects as well as term_t values are references to references to terms.
>The comparision functions - PlTerm::operator==, PlTerm::operator> and so
>on - are wrong in this case. They don't compare the values of PlTerm
>objects (which are indexes for the local stack), but the terms refered to
>by the term references refered to by the indexes. [ Isn't declarative
>programming much less of a headache than this? ;-) ]
>
>For instance, if you stored PlTerms in a sorted container, such as a
>map<>, you could affect the comparision order of the PlTerms by operations
>which either change the terms referred to indirectly, or the term
>references refered to. You would mess up the container as a side effect.
>This would make it impossible to store PlTerms in sorted containers.

Hmm.  This is getting tricky.   The relation between values and pointers
as seen in C(++) is quite different in Prolog.   A PlTerm should be seen
as a variable that can hold Prolog terms and that can be assigned using
one of the PL_put_*() functions.  Prolog terms can be references to
`the real thing' and so on, but how many indirections there are should
not matter to the programmer, whether in C(++) or in Prolog.  Prolog
terms are fully ordered using the standard order of terms and therefore
the definition of ==, <=, <, >= and > as C++ operators exploiting this
appears natural.  Unification is an alien operation to C++ and
therefore regarding it some sort of assignment using the = operator has
been a wrong choice (agreed).

I know next to nothing about STL and I'm wondering how much we have to
sacrifice and what we get in return making PlTerm's operators compliant
to STL.  Do you have any scenarios with nice looking STL code doing
plausible things with Prolog terms?  Normally, all you do is create
terms, call predicates and map the results to something you want to
use in C++.  Lifetime of Prolog terms from C++ are a bit hard to grasp
for the simple soul and operating them is far easier in Prolog.  This
makes manipulation of them in C++ a rather obscure thing. You *could*
do things like ``call P1, manipulate the result in C++ and then call
P2'', but this is a quite unlikely scenario: about all you could
possibly do in between can be better done in Prolog.   Note that you
can use PL_put_* only on PlTerms you created yourself, *not* on the ones
you get as predicate arguments.

>In order to fix that, one could rename PlTerm to PlTermRefRef, and make
>the comparision methods compare the term_t values (the stack indexes) the
>objects contain. This way, PlTerm objects would behave exactly like
>pointers of some type Term**.

Sounds awfully complicated, telling the users about PlTerm is already
complicated enough.  If we have to tell the poor C++ user there are
three versions of them ...  Besides, its not true.  If I do

  term_t x = PL_new_term_ref();
  PL_put_atom_chars(t, "hello");

x is an offset in the local stack and at that location *is* the atom
'hello' (no, not the text, an atom is an integer with some tag-bits
and an offset into the atom-table).

>Additionaly, one could introduce a class named PlTermRef, which behaves
>like a term reference. That is, like a pointer to terms, instead of a
>pointer to pointers to terms. One would have to make the object a unique
>representation of its term reference. One would have to hide the stack
>index inside, so the term reference can't be manipulated except via the
>PlTermRef object.
>
>> Maybe some C++ experts can make a neat SWI-cpp.h that complies with
>> STL and uses templates?  My C++ knowledge is too limited.  I can help
>> with correct implementation though.
>
>I've taken a look at SWI-cpp.h. It doesn't look very difficult to do this.
>I'd volunteer to do the C++ things.

I'm looking forward to a new SWI-cpp.h that makes things look nicer. 
Getting rid of PlTermv would be a good start.  Before doing so, make
sure you understand all issues around term_t.

Maybe you can start a page on the twiki web holding issues and the
current version?  I do propose to use a different name, so old code
can keep using SWI-cpp.h.  If you, like SWI-cpp.h, limit the code
doing the work (which isn't much) as inline functions there should
be no problem having multiple implementations.

	Cheers --- Jan