ClassesWithModels.cpp

// ClassesWithModels.cpp
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2011

@import "Ceda/cxObject/PrintReflectedType.h"
@import "Ceda/cxObject/PrintReflectedVariable.h"
@import "Ceda/cxObject/IObjectVisitor.h"
@import "ValidateSerialisation.h"
@import "ExampleUtils.h"
#include <math.h>

/*
Modifiers in << >>
------------------

    ser             Read/Write to archive can be reflected
                    default = true
    
    os              Generate operator<<() for xostream in terms of MyClass::Write()
    
                    A class has "access to a model" if any of the following are true
                        -   it is a pure model
                        -   it references a pure model
                        -   it embeds an anonymous model
    
                    In that case by default operator<<() is generated
    
                    default = false
    
    ar              Generate operator<<(),operator>>() for Archive in terms of MyClass::Serialise()
                    default = false
                    
    extern          Class definition is provided elsewhere, and therefore this definition
                    should be elided.
                    This is used to reflect pre-existing classes, such as the Guid datatype
                    which is predefined in cxUtils.
                    default = false
                    
    passbyvalue     Allow pass by value in ABI
                    This sets the bit flag EReflectedClassBits::RCF_ALLOW_PASS_BY_VALUE
                    when the class is reflected
                    default = false
    
    returnbyvalue   Allow return by value in ABI
                    This sets the bit flag EReflectedClassBits::RCF_ALLOW_RETURN_BY_VALUE
                    when the class is reflected
                    default = false
                    
    multiline       Auto generated function to write model to an xostream uses the 
                    multiline format
                    default = false
                    
Multiline
---------

<<multiline>> is supported on models to make xcpp generate a multiline version of operator<<() 
which writes it to an xostream.  This is appropriate for complicated models

E.g.

    $model Point { float32 x; float32 y; };

    // required
    #include "Ceda/cxUtils/ScopeWriter.h"

    $model X <<multiline>>
    {
        int32 a;
        int32 b[2][2];
        Point c[];
    };

E.g. a value of X may be written to an xostream like this

    X
    {
        a = 10
        b = [[0,0],[22,-11]]
        c = [Point(1,2),Point(0,0) ,Point(4,-1)]
    }

Note that the textual output is meant to be compatible with the parser format used by 
cxCedaScript, irrespective of whether multiline output is used.

Currently variants upset that assumption because the output isn't 'tagged' for anonymous types 
declared in a variant.  E.g.  a variant may output 123 instead of int32(123).  The current 
plan is to fix this eventually by making cxCedaScript smarter at parsing variants.
*/

///////////////////////////////////////////////////////////////////////////////////////////////////
// EmbeddedModels1
/*
A model can be embedded into a $class/$struct.  The model actually appears as a member named 
_model_ and this allows for direct access to the model.

Xcpp generates access methods on the model which allow for certain system provided functionality.
Such access to the model does the following:

    -   read access invokes the DGS read barriers
    
    -   write access invokes the DGS write barriers
    
    -   mutative functions invoke the callbacks needed to support
    
        -   Operational Transformation
        
        -   Marking objects as dirty as required for cxPersistStore
        
        -   Allocation of oids to pref'd objects

Serialisation of the RSN
------------------------

The generated serialise functions for a $class/$struct with an embedded model begin by serialising
the Release Sequence Number (RSN) of the target in which the $class/$struct is defined.

This provides a basis for schema evolution of the embedded model, plus any other models that are
serialised as well.

    void C::Serialise(ceda::Archive& _ar) const
    {
        ceda::WriteDataSourceRsn(_ar,ceda::s_localRsn);
        _model_.Serialise(_ar);
    }
    void C::Deserialise(ceda::InputArchive& _ar)
    {
        ceda::ReadDataSourceRsn(_ar,0);
        _model_.Deserialise(_ar);
    }
*/
namespace EmbeddedModels1
{
    // $class/$struct can use an anonymous model
    $struct C :
        model
        {
            int32 x;
        }
    {
        /*
        Shows how direct access to the model is supported        
        */
        void MakesDirectAccess()
        {
            _model_.x = 10;
        }
    };

	void Run()
	{
		ceda::TraceGroup g("EmbeddedModels1");
		
		/*
		Idealy the size of a $class/$struct would match the size of the embedded model.  However 
		the current implementation takes up an extra 4 bytes (on Win32 x86 builds) because of
		the way it utilises a union of 'attribute' structs used to hook into the read/write
		accesses to the members.  This is actually wasted space.

		output
		    Size of C = 8 bytes    
		*/
		Tracer() << "Size of C = " << sizeof(C) << " bytes\n";

		/*
		A $class/$struct with an embedded model has operator<<() to an xostream written, just as
		for pure models.
		
		output
		    c = C(0)
		*/
		C c;
		Tracer() << "c = " << c << '\n';
		
		/*
		The read of c.x in the RHS expression invokes the DGS read barrier (i.e. 
		ceda::DataSourceReadBarrier())

		The assignment to c.x invokes the DGS write barrier (i.e. ceda::DataSourceWriteBarrier())
		
		These DGS read/writer barriers have no effect here, but in other situations allow for the
		members of the model to behave as independent nodes of the DGS
		
		output
		    c = C(1)
		*/
		c.x = c.x + 1;
		Tracer() << "c = " << c << '\n';

		/*
		read() is available on each member of the model, and for a member of type T, 
		it invokes the DGS read barrier and returns the member as a const T&.
		
		output
		    c.x.read() = 1
		*/
		Tracer() << "c.x.read() = " << c.x.read() << '\n';
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// ClassRefsModel
/*
A $class/$struct can reference a model by name
*/
namespace ClassRefsModel
{
    $model Point
    {
        int32 X;
        int32 Y;
    };

    $struct C : model Point
    {
    };

	void Run()
	{
		ceda::TraceGroup g("ClassRefsModel");
		
		/*
		output:
		    Size of C = 12 bytes
		*/
		Tracer() << "Size of C = " << sizeof(C) << " bytes\n";

		C c;
		c.X = c.X + 1;
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// ModelsEmbeddedInMixins
namespace ModelsEmbeddedInMixins
{
    $mixin M :
        model
        {
            float64 N;
        }
    {
    };
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Syntax
namespace Syntax
{
    struct Base1 {};
    struct Base2 {};
    struct Base3 { Base3(int x,int y) {} };
    struct Base4 {};

    $model+ SomeModel {};
    $mixin MyMixin1 {};
    $mixin MyMixin2 {};

    /*
    Base1, Base2, and SomeModel are fed into the mixin chain (which can be very handy but it assumes 
    Base1, Base2 have default constructors).  Base3 and Base4 are instead direct base classes of C3
    and may not have default constructors
    */

    $class+ C3 isa ceda::IObject : ceda::EmptyBase,Base1,Base2 model SomeModel mixin [MyMixin1 MyMixin2] Base3,Base4 : [1,2,3,foo(x)]
    {
        C3() : Base3(0,0) {}
    };
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Paths on members of transient models are supported
namespace PathsOnTransientModels
{
    $struct Foo << register >> isa ceda::IObject :
        model
        {
            ptr< ceda::IObject > X;
            ceda::xvector< ceda::string16 > Y;
        }
    {
        void DoSomething()
        {
            ceda::FieldPath p1 = X.path();
            ceda::FieldPath p2 = Y.path();
        }
    };
}

namespace ClassesWithModels
{
    void Run()
    {
        EmbeddedModels1::Run();
        ClassRefsModel::Run();
    }
}