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

From: Jan Wielemaker <[email-masked]>
Date: Mon, 5 Nov 2001 11:46:18 +0100

On Mon, 05 Nov 2001, Volker Wysk wrote:
>On Sun, 4 Nov 2001, Jan Wielemaker wrote:
>
>> > The class PlTerm in the C++ interface, SWI-cpp.h, contains this:
>> >
>> >   int operator =(const PlTerm &t2)	/* term */
>> >   { return PL_unify(ref, t2.ref);
>> >   }
>> >
>> > This is not correct copy semantics. You can't overwrite a PlTerm
>> > object. You can't use PlTerm objects as values like this. For
>> > instance, the Standard Template Library relies on correct copy
>> > semantics. You couldn't store PlTerm's in STL containers, etc.
>> >
>> > I suggest renaming the operator=() methods to unify().
>>
>> I never claimed to be a C++ expert.  = has been redefined to allow
>> natural unification from C++.  I guess you'd like to see
>>
>>    int operator =(const PlTerm &t2)	/* term */
>>    { return PL_put_term(ref, t2.ref);
>>    }
>
>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.

>PlTerm objects are lightweight, and it makes perfect sense to use them as
>values (you wouldn't want allocate them on the heap). In order to do this,
>they must behave like values, or unforeseen things will happen. So an
>assignment must make two objects identical. I'm not sure if this is
>completely guaranteed after the PL_put_term() operation.
>
>I'd just do "ref = t2.ref;" instead of the call to PL_put_term. This is
>what the default assignment operator would do. So you could just leave
>away the operator= method.
>
>Also, the return type isn't right; it needs to be PlTerm&. For instance,
>for statements like this: if ((term1 = term2) > term3) { ...
>
>> I think I agree to your proposal, also because = is a bit strange
>> and leads to
>>
>> 	return X = Y;
>>
>> rather then the possibly someway clearer:
>>
>> 	return X.unify(Y);
>>
>> Big problem is that there is a lot of code around that will break
>> if we change this.
>
>I think, the assignment behavior should be changed. The way it is now, it
>can be a great source of errors. It's just too obvious to use PlTerm
>objects as values, like you would use pointers.
>
>There are more things that could be improved. For instance, I found it
>confusing that the indexes of PlTerm::operator[] start at 1, whereas in

This is to stay compatible with Prolog argument counting.  Unclear
whether that is good or bad.

>PlTermv::operator[] they start at 0. I'd abandon the PlTermv class
>altogether, and use vector<PlTerm> instead; or iterators.
>
>I'd also use C++ strings instead of, or in addition to C strings. The
>entire thing could also be better intergreted with the STL. For instance,
>Prolog lists could be mapped to list<>. The conversions from/to PlTerm
>could also be done more elegantly with template methods, or traits. This
>would make it extensible, so you could add your own conversions without
>changing the PlTerm class.

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.

>Btw. there's a small bug: PlTerm::functor() (as described in the
>documentation) is PlTerm::name() in SWI-cpp.h.

Thanks.  I'll fix that.

	--- Jan