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.