
Basile Starynkevitch wrote:
A newbie question regarding the C API. What is the pointer ownership convention, and where could I find tiny examples or tests of the C API of PPL?
The C language interface of the PPL is meant to replicate, as closely as possible, what is available in the C++ language interface.
Usually, the C++ interface provides for each of its datatype:
- (copy) constructors ==> (in C) ppl_new_* - copy assignment ==> (in C) ppl_assign_* - destructor ==> (in C) ppl_delete_*
with the semantics usually adopted when defining so-call "concrete types" having value (not reference) semantics. Unless otherwise stated, copy construction and assignment are implemented as _deep_ copy operations and provide no reference counting. (NOTE: a form of reference counting is implemented for the elements of powerset objects.)
Regarding ownership:
1) After a call to a ppl_new_<TYPE>* method, the caller has to take responsibility of the newly created object (usually, the first argument of the call, explicitly passed as a pointer, i.e., ppl_<TYPE>_t*). That is, the rule of thumb is that you get ownership if there is the "new" in the function name. As an example of a resource that you do *not* own is the resource pointed by pcs after a call to function:
int ppl_Polyhedron_get_minimized_constraints (ppl_const_Polyhedron_t ph, ppl_const_Constraint_System_t* pcs);
2) The one and only way to release a resource r *that you are owning* is by calling ppl_delete_<TYPE>(r).
3) Except for case 2) above, ownership is preserved for all input arguments passed as ppl_<TYPE>_t or ppl_const<TYPE>_t. No matter if these arguments are modified or not during the call, the responsibility for their resources does not change (i.e., typically the caller will be responsible for calling ppl_delete at some time).
Some of the constructors for PPL objects avoid the need of deep copies. For instance, we have
int ppl_new_C_Polyhedron_recycle_Constraint_System (ppl_Polyhedron_t* pph, ppl_Constraint_System_t cs);
This will build a new polyhedron *reusing* the data structures provided in the input argument cs, thereby avoiding the expensive copy.
BEWARE: after calling the function above, the input argument cs is still a well behaved Constraint_System object owned by the caller: you simply cannot predict its contents, which might have changed. That is, the caller is still responsible of properly destroy cs if it is no longer needed, by calling
ppl_delete_Constraint_System(cs);
So, I made a ppl_Coefficient_t (let's call it k) using ppl_new_Coefficient_from_mpz_t. [BTW, perhaps a ppl_new_Coefficient_from_long_long or a ppl_new_Coefficient_from_int32 might be useful]
Then I am filling a ppl_Linear_Expression_t (let's call it l) using ppl_Linear_Expression_add_to_coefficient with the same coeffiient k.
Should I explicitly delete by calling ppl_delete_Coefficient(k), or is k now "owned" by l and not available anymore?
As said above, a _copy_ of k has gone into l. k is still owned by you: you can reuse it, assign to it, etc. and finally you have to call ppl_delete_Coefficient(k).
Could I use twice the same k in different calls to ppl_Linear_Expression_add_to_coefficient?
Yes.
I would suppose that PPL use a refcounting scheme as its memory management (or garbage collection) scheme. Is it correct? Who/when are the refcounters incremented & decremented? Is there a C API to e.g. force incrementation of the refcounter (like GTK has)?
No, usually there is no reference counting scheme. As said above, we have a reference counting in our Powerset datatypes, but this is transparent to the users of all the language interfaces.
More generally, you can regard the C interface as providing the basic services around which the client application can build its own abstractions. Reference counting schemes are one such possibility.
BTW, a more general question is about the philosophy of memory management in PPL, especially when interfacing PPL to some language. Since my GCC MELT can be percieved as some language (the MELT lisp dialect) with its runtime (the MELT garbage collector, above the GGC inside GCC), I could view the interfacing of PPL inside MELT as a language binding of PPL.
Not sure we understand the question. The basic design principles of the PPL interfaces are:
1) try to provide interfaces that are "natural" in the interfaced language; 2) do the basic things efficiently and do not interfere with further abstractions user may want to implement.
In particular, detailed explanation of Ocaml binding to PPL is welcome, since I happen to know quite well Ocaml (and the C coding rules required by its runtime).
We suggest you look into the library's manuals first (core, C interface and OCaml interface). If something is missing there, please let us know.
For the C interfaces, and additional source of examples is the ppl_lpsol program in demos/ppl_lpsol. Cheers,
Enea and Roberto