# 17 Situated Cell-Like Objects

In this chapter, we implement `class` `Celloid`s: an extension class for objects that behave essentially like cells; they have content which can be accessed (read) and assigned (written). The new challenge here is twofold: (1) during garbage collection the content of a `class` `Celloid` must also be copied, (2) we must ensure that only a local `class` `Celloid` can be mutated (for a non-local one, we should signal an error).

A `class` `Celloid` is situated. What does situated mean? Simply that the object resides in one specific constraint store, aka a computation space. If `class` `Celloid` `C` resides in space S1, and S2 is a subspace of S1 (i. e. is subordinated to S1), it is meaningful for a thread in S2 to access `C` since any constraint (therefore value) from S1 are visible in S2, but it is generally not meaningful for a thread in S2 to assign `C` since constraints (therefore values) specific to S2 are not visible from S1. Our implementation will enforce the appropriate restrictions.

## 17.1 Celloid Class

Again, we subclass `class` `OZ_Extension`.

  `#include "mozart.h"
  class Celloid : public OZ_Extension {
  public:
  OZ_Term content;
  Celloid(OZ_Term t):content(t){}
  static int id;
  virtual int getIdV() { return id; }
  virtual OZ_Term typeV() { return OZ_atom("celloid"); }
  virtual OZ_Extension* gCollectV();
  virtual OZ_Extension* sCloneV();
  virtual void gCollectRecurseV();
  virtual void sCloneRecurseV();
  virtual OZ_Term printV(int depth = 10);
  };`

## 17.2 Celloid Creation

The `celloid_new` builtin takes one input argument `t`, creates a new celloid whose content is initialized to `t`, boxes it and returns the result.

  `OZ_BI_define(celloid_new,1,1)
  {
  OZ_declareTerm(0,t);
  OZ_RETURN(OZ_extension(new Celloid(t)));
  }
  OZ_BI_end`

## 17.3 Type Testing

The definitions here are similar to the ones presented earlier for the `class` `Counter` class.

  `int Celloid::id;
  inline OZ_Boolean OZ_isCelloid(OZ_Term t)
  {
  t = OZ_deref(t);
  return OZ_isExtension(t) &&
  OZ_getExtension(t)->getIdV()==Celloid::id;
  }
  OZ_BI_define(celloid_is,1,1)
  {
  OZ_declareDetTerm(0,t);
  OZ_RETURN_BOOL(OZ_isCelloid(t));
  }
  OZ_BI_end`

## 17.4 Expecting Celloid Arguments in Builtins

Again this is similar to the `class` `Counter` class: we define an unboxing function and a convenience macro.

  `inline Celloid* OZ_CelloidToC(OZ_Term t)
  {
  return (Celloid*) OZ_getExtension(OZ_deref(t));
  }
  #define OZ_declareCelloid(ARG,VAR) \
  OZ_declareType(ARG,VAR,Celloid*,"celloid",OZ_isCelloid,OZ_CelloidToC)`

## 17.5 Operations on Celloids

First, we provide an access builtin that retrieves the content of the celloid.

  `OZ_BI_define(celloid_access,1,1)
  {
  OZ_declareCelloid(0,c);
  OZ_RETURN(c->content);
  }
  OZ_BI_end`

Second, we provide an assign builtin that sets the content of the celloid. This operation should only be allowed for a thread executing in the home space of the celloid. For a thread executing in a subordinated space, an exception will be raised.

  `OZ_BI_define(celloid_assign,2,0)
  {
  OZ_declareCelloid(0,c);
  OZ_declareTerm(1,t);
  if (c->isLocal()) { c->content=t; return PROCEED; }
  else return OZ_raiseErrorC("celloid",3,OZ_atom("nonLocal"),
  OZ_in(0),OZ_in(1));
  }
  OZ_BI_end`

virtual member function `isLocal()` indicates whether the current space is the home space of the celloid. If yes, we set the content to the given argument; if no, we raise an error. `OZ_in(`n`)` refers to the nth input argument of the builtin.

## 17.6 Printing Support

We provide here only minimal printing support.

  `OZ_Term Celloid::printV(int depth = 10)
  {
  return OZ_atom("<celloid>");
  }`

## 17.7 Garbage Collection

The first part of garbage collection is as before: we create a new instance of `class` `Celloid` initialized with the current content of the celloid that is being copied by gc.

  `OZ_Extension* Celloid::gCollectV() { return new Celloid(content); }`

The second part involves recursively copying the content of the celloid. This is implemented in a different virtual function:

  `void Celloid::gCollectRecurseV() { OZ_gCollect(&content); }`

The procedure `OZ_gCollect(OZ_Term*)` performs the gc copy and update of its argument.

You may wonder: why not perform the recursive copy of the content in `gCollectV()` itself. Under no circumstances should you do this! It would break essential invariants in the garbage collector. GC copy must proceed in these 2 phases:

1. `gCollectV()` creates a new instance (on the to heap) and initializes it with the current contents of the object being gced.

2. `gCollectRecurseV()` is at some subsequent point invoked on the new instance and should perform the gc copy and update of its contents.

## 17.8 Cloning

Cloning is used to produce a copy of a computation space. It has the same structure, and the underlying implementation in fact shares most of the code with, garbage collection.

  `OZ_Extension* Celloid::sCloneV() { return new Celloid(content); }
  void Celloid::sCloneRecurseV() { OZ_sClone(&content); }`

## 17.9 Native Functor

Again, we proceed as before:

  `OZ_C_proc_interface * oz_init_module(void)
  {
  static OZ_C_proc_interface table[] = {
  {"new",1,1,celloid_new},
  {"is",1,1,celloid_is},
  {"access",1,1,celloid_access},
  {"assign",2,0,celloid_assign},
  {0,0,0,0}
  };
  Celloid::id = OZ_getUniqueId();
  return table;
  }`

Assuming the code above is put in file `celloid.cc`, we first compile and then create a DLL as follows

> `oztool c++ -c celloid.cc
> oztool ld celloid.o -o celloid.so-`oztool platform``

## 17.10 Oz Wrapper Module

Here, we hardly need an Oz wrapper module. Unlike for counter objects, we don't need to register celloid for finalization: there are no resources off the heap. However, we can provide a nice error print formatter for the case when an access is attempted from without the celloid's home space.

  `functor
  import
  Celloid(new:New is:Is access:Access assign:Assign)
  at 'celloid.so{native}'
  Error(registerFormatter)
  export
  New Is Access Assign
  define
  fun {CelloidFormatter E}
  T = 'Celloid Error'
  in
  case E of celloid(nonLocal C V) then
  error(kind: T
  msg: 'Attempted assign on non local celloid'
  items: [hint(l:'Operation' m:'Celloid.assign')
  hint(l:'Celloid' m:oz(C))
  hint(l:'Value' m:oz(V))])
  else
  error(kind: T
  items: [line(oz(E))])
  end
  end
  {Error.registerFormatter celloid CelloidFormatter}
  end`