Implement an IPersistable object

Without a model

The following class allows for an object to be represented in a database having an int32 member.


$struct+ X isa IPersistable
{
    void Serialise(Archive& ar) const 
    {
        ar << x;
    }
    void Deserialise(InputArchive& ar) 
    {
        ar >> x; 
    }

    int32 x;
};

In this case an embedded model is not used, so the following features aren't available on the members:

One possible reason for not using a model is to allow for sites to have divergent copies of the object. However, that is often better achieved by using objects with models in another working set which is not synchronised in the first place.

Often it makes sense to define an IPersistable class without a model because it's immutable, and uses the <<rod>> directive to declare it as a ROD (Replicate on Demand) object type.

In the following case a JPeg image object persists as a vector of bytes. This can be decoded to provide an image. The buffer would only be updated in the transaction that created the original instance of the JPeg object. After that it would never be changed. When these objects are used in a WorkingSet their initial state is serialised when they are replicated to other sites.


$struct+ JPeg <<rod>> isa IPersistable
{
    void Serialise(Archive& ar) const 
    {
        ar << buffer;
    }
    void Deserialise(InputArchive& ar) 
    {
        ar >> buffer; 
    }

    xvector<octet_t> buffer;
};

With a model

Now we have an IPersistable class with a model:


$struct+ X isa IPersistable
    model
    {
        int32 m;
    }
{
};

The plan is to support the following syntax:


$object+ X
{
    int32 m;
};

In this case a model is used, so the following features are available on the members:

The member variable m has assignment semantics. Operations on m must be pure assignments, so for example you can't use m++ to increment m, or m *= 2 to double the value of m.

Let x be a variable of type X.

The following read of x.m invokes the DGS read barrier on x.m. If this is done while calculating a dependent variable then a dependency edge from x.m to the $dep variable is created.


int32 y = x.m;

If x is a persistent object under the Universal Tree in a WorkingSet then the following assignment to x.m generates an assignment operation which can be propagated to peers over a network connection in order to apply the same assignment on a replica of the WorkingSet.


x.m = 10;

This assignment to x.m also invokes the DGS write barrier on x.m. This may cause the dependents of x.m to be invalidated.

Attempting any of the following updates to x.m will produce compiler errors.


++x.m;
x.m++;      
x.m += 2;
x.m -= 2;
x.m *= 2;
x.m /= 2;
x.m %= 2;

On Windows you get error C3892: 'x': you cannot assign to a variable that is const. Unfortunately this isn't an ideal error message, but it is to be expected given the fact that Xcpp is a rather lightweight preprocessor and a standard C++ compiler is doing a lot of the error checking.

Basic types default to assignment semantics

The basic types bool, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, char8, char16 default to assignment semantics.

This means they can only be updated using assignment operations, and under Operational Transformation conflicting concurrent assignments to the same variable are resolved by choosing one of the assignments as the "winner" (this is based on site identifier comparisons).