On Fri, Feb 22, 2013 at 8:40 AM, Kurt Starsinic <[email protected]> wrote:
> For a little context, the history constraint isn't mentioned in Liskov's
> original paper <http://doi.acm.org/10.1145/62139.62141> (1987); it first
> appears in Liskov and Wing <http://doi.acm.org/10.1145/197320.197383>(1994).

Material for the Book Club?

After a brief skim, my impression is that while both are interesting, the
second paper is more cohesive and a better read.

> And, having said all that, it seems to me that making CharBuf a subclass of
> String is a square-peg/round-hole situation.

The reverse -- having String subclass CharBuf -- is problematic as well.  If
the subtype is immutable, then casting to the mutable superclass is akin to
casting away const-ness.

This issue comes up when wrapping a host argument string to pass to a function
in Clownfish-space.

If the Clownfish-space function declares that it accepts a `const CharBuf*` as
an argument, then it's safe to provide string content owned by the host object
within a stack-allocated wrapper.

    // Implemetation of Clownfish-space function.
    void
    Foo_set_name(Foo *self, const CharBuf *name) { // <--- `const`
        CharBuf *temp = self->name;
        self->name = CB_Clone(name);  // <--- Clone(), not INCREF.
        DECREF(temp);
    }

    // Wrapper function.
    void
    wrap_Foo_set_name(SV *foo_sv, SV *name_sv) {
        Foo *self = (Foo*)extract_cfish_obj_from_sv(foo_sv, FOO);
        STRLEN len;
        const char *name_str = SvPVutf8(name_sv, len);
        void *stack_memory = alloca(ZCB_size());
        ZombieCharBuf *name = ZCB_wrap(stack_memory, name_str, len);
        Foo_set_name(self, name);
    }

The Clownfish-space function cannot store a reference to the `const CharBuf*`,
because that requires taking ownership of a refcount -- and incrementing a the
refcount requires mutating the object, violating the `const` constraint.
(The rules for passing around a `const CharBuf*` are the same as for passing
around a `const char*`.)

However, if the Clownfish-space function declares that it takes a non-const
`CharBuf*` as an argument, we need to malloc a whole new CharBuf and copy the
host string into it -- and then after the Clownfish-space function returns, we
need to release its refcount[1].

Allocating on the heap is essential because the Clownfish-space code may
INCREF the CharBuf and store a reference which outlives a stack-allocated
wrapper.

    // Implemetation of Clownfish-space function.
    void
    Foo_set_name(Foo *self, CharBuf *name) { // <--- not `const`
        CharBuf *temp = self->name;
        self->name = (CharBuf*)INCREF(name); // <--- INCREF, not Clone().
        DECREF(temp);
    }

    // Wrapper function.
    void
    wrap_Foo_set_name(SV *foo_sv, SV *name_sv) {
        Foo *self = (Foo*)extract_cfish_obj_from_sv(foo_sv, FOO);
        STRLEN len;
        const char *name_str = SvPVutf8(name_sv, len);
        CharBuf *name = CB_new_from_trusted_utf8(name_str, len);
        Foo_set_name(self, name);
        DECREF(name);  // <--- Dispose of refcount.
    }

Surprisingly, the need to wrap host string arguments, which is a significant
aspect of Clownfish, does not argue for an immutable String class -- it argues
for `const`!

Only the _content_ of a non-const String is immutable, not its refcount -- so
only a `const String*` can replace `const CharBuf*` in our type-mapping
scheme.

Marvin Humphrey

[1] The refcount will leak if an exception is thrown in Clownfish-space
    because we do not have control over unwinding the stack.

Reply via email to