PlTerm
is a struct that contains a term_t
(as if it were declared struct { term_t C_; }
in C), with additional âmethodsâ. So, auto t_low = A1
is the essentially the same as PlTerm t_low=A1
, which simply copies the term_t
value. In C++, when t_low
goes out of scope, it calls the destructor â currently the destructor does nothing but @jan has proposed that it call PL_free_term_ref()
. To do this correctly would require reference counting, I think.
A variant of assignment is PlTerm t_low(A1)
â this also creates a new object that wraps a term_t
, but uses the âcopy constructorâ instead of assignment; the net result is the same as auto t_low=A1
. A copy constructor is also invoked when an object is passed as an argument to a function, or as a return value. (In theory, a copy constructor could be different from the assignment operator, but in practice theyâre usually the same.) There is also a âmove assignmentâ and âmove constructorâ which allow defining an optimized version of copying for certain situations â SWI-cpp2.h
doesnât define any move operations, so they default to the copy operations.
C++ allows overriding the copy operations that are done by assignment and function calls. Itâs also possible to define a class that doesnât allow copying â thatâs what I was thinking of with the PlTerm_new
constructor that would have a destructor that calls PL_free_term_ref()
.
C++ has two âsmart pointersâ that wrap an ordinary C pointer and handle automatic deletion: shared_ptr
and unique_ptr
(they both override assignment and the âcopy constructorâ). Shared_ptr
has a reference count, so the underlying pointer is deleted only when its reference count goes to zero. Unique_ptr
ensures that a pointer has only a single âownerâ, by transferring ownership on copy and zeroing the source â it doesnât allow assignment. For example:
std::unique_ptr<MyStruct> p(new MyStruct);
std::unique_ptr<MyStruct> q;
// Not allowed: q = p;
q.reset(p.release());
is roughly equivalent to:
MyStruct *p = malloc(size of MyStruct);
MyStruct *q;
q = p; p = 0;
...
free(q);
free(p); // Does nothing because q==0
My proposal of PlTerm_new
is that it would have semantics similar to unique_ptr
. (Unique_ptr
has a few other operations: get()
, which is dangerous and accesses the underlying pointer; release()
, which explicitly transfers ownership; reset()
, which sets the underlying pointer.) Itâs relatively straightforward to implement these correctly.
Alternatively, we could make PlTerm
into a reference-counted term_t
. Itâs trickier to get everything correct, but it can be done. (The source code for shared_ptr
is fairly unreadable, because itâs quite general; but there exist some simplified implementations, such as GitHub - SRombauts/shared_ptr: A minimal shared/unique_ptr implementation to handle cases where boost/std::shared/unique_ptr are not available.)
As an alternative, it might be possible to simply document how to use unique_ptr
and shared_ptr
to do what we want (both of these can define a custom âdeleterâ, for example). Iâd have to think a bit about that possibility.