This is the mail archive of the gsl-discuss@sourceware.org mailing list for the GSL project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

final (?) word on constness and containers



Once more on the container constness problem and
container design, to express my final understanding
of the problems and suggest a path forward.

I have concluded that the design of the vector and
matrix view types is completely brain-damaged. The
two distinct types (non-const and const) are useless,
because they cannot enforce their implied contract.

Any client who uses the const-ified view type is forced
to cast away the constness before using any GSL interface.
So the const types are actually just an annoying trap.


The full vector and matrix types are slightly less
brain-damaged. They also do not enforce any implied
contract, but they do not really claim to do so,
since there is no const-ified type. There is
only the current illusion of const-correctness.

I have spent some effort trying to understand the
state of the art for containers in C. The conclusion
seems to be that const-correctness is essentially
impossible in practical C code of this type. This
is quite sad, when you think about it. But that's
the way it is.

On the bright side, you could argue that this situation
is inevitable in an inter-language environment, where GSL
might be wrapped in python, etc. The notion of constness
is C-language-family specific and does not translate
well to dynamic inter-language contexts.


So here are my recommendations:

 R1) Introduce a set of "view constructors" which
     build gsl_vector objects (not views) that
     wrap existing memory. Objects are built
     on the stack.

     Examples:
       gsl_vector gsl_vector_as_view(double * x, size_t size, int stride);
gsl_vector gsl_vector_as_subvector(const gsl_vector * v, size_t i, int stride, size_t n);

     The notion of "view" shifts from a distinct type
     to flexible methods of construction for the
     single main type.

     For parallelism, introduce stack constructors for
     heap-allocated data as well:
       gsl_vector gsl_vector_as_heap_data(size_t size);

     This is important, to encourage a more value-centric
     philosophy (rather than pointer-centric), which makes
     client code more flexible and probably more maintainable.

     The pointer-based idioms are retained, since they
     are also useful, especially in more dynamic contexts.
     Also, current client code depends on them.

 R2) Expand the semantics of the 'owner' member in the vector
     and matrix structs to be flags that delineate the supported
     actions in gsl_XXX_free(). Currently, the value is either 0 or 1,
     where 0 instructs gsl_vector_free() to not attempt to free the
     data segment. The new values and their semantics would be as follows:

     GSL_OWNER_EXTERN      = 0 /* wrapper and data externally managed */
     GSL_OWNER_MALLOC_DATA = 1 /* data is heap allocated */
     GSL_OWNER_MALLOC_WRAP = 2 /* wrapper is heap allocated */
     GSL_OWNER_MALLOC_BOTH = 3 /* (1 & 2) */

     gsl_XXX_free will test these flags and act accordingly.

     The values are not quite binary compatible with the
     current design, because the current constants were
     badly chosen.

 R3) Deprecate the current view functionality. There is
     no reason to eliminate it, since it is orthogonal
     to everything, and some clients may be burdened
     if we removed it immediately.


Consequences:

 C1) The constness problem is not solved. This is the only
     way forward that does not change everything. The
     library can handle the casting away of constness
     at construction time, continuing to propagate the
     current fiction that the design is const correct.

 C2) Clients gain more flexibility in managing their own
     memory and creating their own allocation strategies.
     This design is the most flexible and avoids alloc/free
     function wrappers and related "factory" style frameworks,
     which are just too rigid to be contemplated.

 C3) No changes are required in existing client code,
     unless they choose to use the new interfaces.

 C4) Uses of the current "view" types in GSL library code
     should be replaced with new-style constructs. This
     would not be difficult; these internal uses are
     well-delineated and easy to replace. There are,
     however, dozens of them.

 C5) Add the new constructor interfaces and make the
     small needed change to the gsl_XXX_alloc() and
     gsl_XXX_free() impls, to support the new 'owner'
     semantics.


Overall, this seems like a simple and controlled plan.
It's the kind of thing I could bang out in a few
afternoons. And the consequences seem palatable.

Please think hard about how this might impact
future progress. That is the hard problem.

--
G. Jungman


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]