Models.cpp

// Models.cpp
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2009

@import "Ceda/cxPython/cxPython.h"
@import "CedaExamples/lxOperation/Shapes.h"
@import "Ceda/cxObject/Object.h"
@import "Ceda/cxObject/IObjectVisitor.h"
@import "Ceda/cxObject/WCSpace.h"
#include "Ceda/cxUtils/TracerUtils.h"

/*
todo
----

[Daniel Paull]

    I am running Python scripts as part of the recalculation of dependant nodes (an agent 
    actually).  It seems that data source read barriers are not invoked when accessing 
    datasource model members (a feature you added recently).

    For the moment I have modified GetPyObjectFromReflectedModelVariable() in 
    WrapModelStructVar.cpp to call ceda::DataSourceReadBarrier( data, datasource ); 
    at the start of the function, though I am not sure that this is the right way to 
    do this, nor if it is the only place I need to invoke read barriers from.  
    For example, I think some methods in WrapVectorStructVar.cpp will need to invoke 
    read barriers when elements of a vector are accessed.

    I'm not sure about write barriers - they seem to be being invoked (at least in 
    the vector append case), but I could be wrong.

[DBL]
    Write barriers : Note however that I currently don't support dynamic arrays, maps 
    or sets.  Also Python doesn't yet support move operations on mvectors.

[DBL]
    I'm in a quandary as far as read barriers are concerned.  Either it's fully automatic, 
    but potentially inefficient, or else there's some explicit syntax to declare read 
    access to a data model member.

    If the read barriers are called automatically then there are going to be examples 
    such as where millions of redundant dependency graph edges are created because a 
    vector is accessed in a loop.   To check for redundant edges would seem to require 
    iteration through the linked list of edges.

    I was wondering about a 'touch' function that allows the programmer explicit 
    control over read barrier loops...

    Eg

        void bar(const xvector<int>& v) { ... }

        void foo(MyDataSource* ds)
        {
            ds->v.touch();  // declare read access to member v.

            // We don't want to invoke read barrier in the loop!
            for (int i=0 ; i < ds->v.size() ; ++i)
            {
                foo(ds->v[i]);
            }

            // We want to be able to access v as an xvector<int>
            bar(ds->v);
        }

    I'm wondering whether thread local storage can store a bool to indicate a mode for 
    whether or not to automatically invoke the read barriers.  The idea is to get 
    the convenience most of the time, but have the ability to optimise as required.

    Note that access to thread local storage is very fast (making use of a few mov 
    instructions that access the FS CPU register).

[Daniel Paull]

    I have not been able to find an example of a model with a string in it!  Looking at 
    examples/documentation I see "xstring", "vector<int8>" and "string8" used.  The 
    only one that xcpp seems to accept is string8, so I've used that.

    The problem I have is that I get an exception thrown in the destructor of my class 
    when the string is destroyed.  It trips at _CrtIsValidHeapPointer() when trying 
    to free m_buffer from ceda::VectorOfByte.

    Consider the follow snippet.  As written, the problem arises when the model mixin 
    is destructed.  If the "Filename" member is commented out and the anonymous 
    mixin uncommented, there is no problem.  Is there an obvious problem here (like 
    I'm not supposed to use string8 in a model)?  I am using Visual Studio 2008, 
    just in case that causes a problem.

        model
        {
            string8 Filename;
        }
        mixin
        [
            {
                ceda::xstring Filename;
                const ceda::xstring& GetFilename() const
                {
                    return Filename;
                }
                void SetFilename( const ceda::xstring& f )
                {
                    Filename = f;
                }
            }
            ...
        ]
*/

///////////////////////////////////////////////////////////////////////////////////////////////////
/*
Models 1
--------

Generated output:

    Models example 1
    {
        x = X(Y(0),0,
        x.i8 = 0
        x.i16 = 0
        x.i32 = 0
        x.i64 = 0
        x = X(Y(1000),1,16,32,64,16,32,64,32,64,0,hello)
        x.i8 = 8
        x.i16 = 16
        x.i32 = 32
        x.i64 = 64
    }
*/
namespace Models1
{
    $enum+ EColour
    {
        red = 0x10, -green, blue
    };

    $model+ Y
    {
        ceda::int32 i32;
    };
    
    $struct+ X <<multiline>> isa ceda::IObject :
        model
        {
            Y y;
            bool b;
            ceda::int8 i8;
            ceda::int16 i16;
            ceda::int32 i32;
            ceda::int64 i64;
            ceda::uint8 ui8;
            ceda::uint16 ui16;
            ceda::uint32 ui32;
            ceda::uint64 ui64;
            ceda::float32 f32;
            ceda::float64 f64;
            
            EColour c;
            assignable<ceda::string8> s1;
            ceda::string8 s2;
        }
    {
    };

    void Run()
    {
		ceda::TraceGroup g("Models example 1");

        ceda::CSpaceCreator cspace;
        ceda::CSpaceLock lock;
        
        X x;

        PyRun_SimpleString(
            @strx
            (
                x = rootnamespace.Models1.X()
                print 'x = ' + `x`
                print 'x.i8 = ' + `x.i8`
                print 'x.i16 = ' + `x.i16`
                print 'x.i32 = ' + `x.i32`
                print 'x.i64 = ' + `x.i64`
                
                print 'type(x) = ' + `type(x)`
                print 'type(x.i8) = ' + `type(x.i8)`
                print 'type(x.i16) = ' + `type(x.i16)`
                print 'type(x.i32) = ' + `type(x.i32)`
                print 'type(x.i64) = ' + `type(x.i64)`
                print 'type(x.ui8) = ' + `type(x.ui8)`
                print 'type(x.ui16) = ' + `type(x.ui16)`
                print 'type(x.ui32) = ' + `type(x.ui32)`
                print 'type(x.ui64) = ' + `type(x.ui64)`
                print 'type(x.f32) = ' + `type(x.f32)`
                print 'type(x.f64) = ' + `type(x.f64)`
                print 'type(x.b) = ' + `type(x.b)`
                #print 'type(x.y) = ' + `type(x.y)`     # todo: crashes
                print 'type(x.c) = ' + `type(x.c)`
                print 'type(x.s1) = ' + `type(x.s1)`

                x.y.i32 = 1000

                x.b = 1
                x.i8 = 8
                x.i16 = 16
                x.i32 = 32
                x.i64 = 64
                x.ui8 = 8
                x.ui16 = 16
                x.ui32 = 32
                x.ui64 = 64
                x.f32 = 32
                x.f64 = 64

                x.s1 = 'hello'

                # These generate errors because the string is assignable, so no vector operations
                #x.s1[2:2] = ' world'
                #x.s1[5:6] = ''
                #x.s1.extend('abc')

                x.s2 = 'hi'
                x.s2[2:2] = ' world'
                x.s2[5:6] = ''
                x.s2.extend('abc')
                
                print 'x = ' + `x`
                print 'x.i8 = ' + `x.i8`
                print 'x.i16 = ' + `x.i16`
                print 'x.i32 = ' + `x.i32`
                print 'x.i64 = ' + `x.i64`
                print 'x.s1 = ' + `x.s1`
                print 'x.s2 = ' + `x.s2`
            ));
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/*
Special support is provided for constructing objects of classes with models.  

Consider

    $model+ Point
    {
        int32 X;
        int32 Y;
    }

Three styles are supported:
                                                                      
1.  The default constructor
    Example: Point()        (same as Point(0,0))

2.  Passing the arguments in the same order that the members are declared in the model
    Example: Point(3,7)

3.  Provide values of any subset of the members in any order using a comma separate list of name=value
    pairs, using the same of the member of the model.
    
    Example: Point(X=3)     (Y will have default value of 0)
             Point(Y=3,X=2)
             Point(Y=1)     (X will have default value of 0)
*/
namespace ModelConstructors
{
    /*
    Generated output:

        ModelConstructors
        {
            p = TPicture
            {
                Name = my picture
                Author = P. Hunt
                CreationDate = TDateTime(TDate(2006,June,23),TTime(0,0,0))
                Colour = magenta
                BoundingBox = TRect(TPoint(0,0),TPoint(800,600))
                Rectangles = [TRect(TPoint(5,8),TPoint(15,12)),TRect(TPoint(8,5),TPoint(10,17))]
                Circles = [TCircle(TPoint(0,0),5),TCircle(TPoint(0,0),2),TCircle(TPoint(100,0),0)]
                Lines = []
            }
        }
    */
    void Run()
    {
		ceda::TraceGroup g("ModelConstructors");
		
		// Make sure lxOperation is linked!
		const ceda::ReflectedClass& rc = ceda::GetReflectedClass<geom::TPoint>();

        ceda::CSpaceCreator cspace;
        ceda::CSpaceLock lock;

        PyRun_SimpleString(
            @strx
            (
                s = rootnamespace.geom
                dt = rootnamespace.dt
                p = s.TPicture(
                        Name = "my picture",
                        Author = "P. Hunt",
                        Colour = s.EColour.magenta,
                        CreationDate = dt.TDateTime( Date = dt.TDate(2006,dt.TMonth.June,23) ),
                        BoundingBox = s.TRect( s.TPoint(0,0), s.TPoint(800,600) ),
                        Rectangles =
                        [
                            s.TRect( s.TPoint(X=5,Y=8), s.TPoint(15,12) ),
                            s.TRect( s.TPoint(Y=5,X=8), s.TPoint(10,17) )
                        ],
                        Circles = 
                        [
                            s.TCircle( s.TPoint(), 5 ),
                            s.TCircle( R=2 ),
                            s.TCircle( C=s.TPoint(X=100) )
                        ]
                    )
                print 'p = ' + `p`
            ));
    }
}

namespace ModelConstructors2
{
    $model+ Point
    {
        ceda::int32 X;
        ceda::int32 Y;
    };

    $model+ Y
    {
        ceda::int32 i32;
        ceda::string8 s;
        ceda::xvector<ceda::int32> L;
    };
    
    $struct+ X isa ceda::IObject :
        model
        {
            Y y;
            ceda::int32 i32;
            ceda::float32 f32;
            ceda::xvector<Y> Ys;
            ceda::xvector< ceda::ptr<ceda::IObject> > M;
        }
    {
    };

    /*
    Generated output:
    
        ModelConstructors
        {
            ns.Point() = Point(0,0)
            ns.Point(X=10) = Point(10,0)
            ns.Point(Y=10) = Point(0,10)
            ns.Point(X=1,Y=2) = Point(1,2)
            ns.Point(Y=1,X=2) = Point(2,1)
            y = Y(10,x,[1,2,3])
            x = X(Y(10,x,[1,2,3]),10,3.14,[Y(1,w,[1,2]),Y(2,e,[])],[ModelConstructors::X:01D7CEF8])
            x.M[0].self = X(Y(10,x,[1,2,3]),1,2,[Y(10,x,[1,2,3])],[])
            z = X(Y(0,,[0,2,4,6,8]),25,0,[],[ModelConstructors::X:01D907B8,ModelConstructors::X:01D90838])
            z.M[0].self = X(Y(0,,[]),1000,0,[],[])
            z.M[1].self = X(Y(0,hello,[]),2000,0,[],[])
        }
    */
    void Run()
    {
		ceda::TraceGroup g("ModelConstructors2");
		
        ceda::CSpaceCreator cspace;
        ceda::CSpaceLock lock;

        PyRun_SimpleString(
            @strx
            (
                ns = rootnamespace.ModelConstructors2

                print 'ns.Point() = ' + `ns.Point()`
                print 'ns.Point(X=10) = ' + `ns.Point(X=10)`
                print 'ns.Point(Y=10) = ' + `ns.Point(Y=10)`
                print 'ns.Point(X=1,Y=2) = ' + `ns.Point(X=1,Y=2)`
                print 'ns.Point(Y=1,X=2) = ' + `ns.Point(Y=1,X=2)`
                                                  
                y = ns.Y(10,"x",[1,2,3])
                print 'y = ' + `y`

                x = ns.X(
                        ns.Y(10,"x",[1,2,3]),
                        10,
                        3.14,
                        [ 
                            ns.Y(1,"w",[1,2]), 
                            ns.Y(2,"e",[]) 
                        ],
                        [
                             ns.X.create(y,1,2.0,[y],[])
                        ]
                    )
                print 'x = ' + `x`

                print 'x.M[0].self = ' + `x.M[0].self`

                z = ns.X(
                        y = ns.Y(L=[2*i for i in range(5)]),
                        i32 = 25,
                        M = 
                          [
                             ns.X.create(i32=1000),
                             ns.X.create(i32=2000,y=ns.Y(s="hello")),
                          ]
                    )
                print 'z = ' + `z`
                print 'z.M[0].self = ' + `z.M[0].self`
                print 'z.M[1].self = ' + `z.M[1].self`
            ));
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/*
VectorsInModels
---------------

    Vectors in models example
    {
        x = X([])
        Demonstration of various read only functions
            x.L =  = [1,2,3,4,5,6,7,8,9]
            bool(x.L) = True
            str(x.L) = ' = [1,2,3,4,5,6,7,8,9]'
            repr(x.L) = ' = [1,2,3,4,5,6,7,8,9]'
            len(x.L) = 9
            min(x.L) = 1
            max(x.L) = 9
            x.L.index(2) = 1
            x.L.index(9) = 8
            x.L.index(15) = -1
            x.L.count(4) = 1
            x.L.count(20) = 0
            x.L*2 =  = [1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9]
            x.L[-1] = 9
            3 in x.L = True
            10 in x.L = False
            3 not in x.L = False
            10 not in x.L = True
            x.L+[100,200] =  = [1,2,3,4,5,6,7,8,9,100,200]
            x.L[3] = 4
            x.L[1:4] =  = [2,3,4]

        Demonstration of various mutative functions
          x.L.extend([10,20,30,40,50])
            x.L =  = [10,20,30,40,50]
          x.L.append(100)
            x.L =  = [10,20,30,40,50,100]
          x.L.insert(0,-1)
            x.L =  = [-1,10,20,30,40,50,100]
          x.L[0] = 4
            x.L =  = [4,10,20,30,40,50,100]
          del x.L[1]
            x.L =  = [4,20,30,40,50,100]
          del x.L[1:3]
            x.L =  = [4,40,50,100]
          x.L[1:1] = [1,2,3,4,5]
            x.L =  = [4,1,2,3,4,5,40,50,100]
          x.L.remove(2)
            x.L =  = [4,1,3,4,5,40,50,100]
          x.L.pop()
            x.L =  = [4,1,3,4,5,40,50]
          x.L.pop(1)
            x.L =  = [4,3,4,5,40,50]
        x = X([4,3,4,5,40,50])
    }
*/
namespace VectorsInModels
{
    $struct+ X isa ceda::IObject :
        model
        {
            ceda::xvector<ceda::int32> L;
        }
    {
    };

    // todo: In Python 2.7, calling 
    //      dir(x.L)
    // in Demonstration of various read only functions
    // causes a crash (but worked in Python 2.4.4)

    void Run()
    {
		ceda::TraceGroup g("Vectors in models example");

        ceda::CSpaceCreator cspace;
        ceda::CSpaceLock lock;

        PyRun_SimpleString(
            @strx
            (
                x = rootnamespace.VectorsInModels.X()
                print 'x = ' + `x`
                
                print 'Demonstration of various read only functions'
                x.L.extend([1,2,3,4,5,6,7,8,9])
                @for (e in 
                [
                    x.L,
                    bool(x.L),
                    str(x.L),
                    repr(x.L),
                    len(x.L),
                    min(x.L),
                    max(x.L),
                    x.L.index(2),
                    x.L.index(9),
                    x.L.index(15),
                    x.L.count(4),
                    x.L.count(20),
                    x.L*2,
                    x.L[-1],
                    3 in x.L,
                    10 in x.L,
                    3 not in x.L,
                    10 not in x.L,
                    x.L+[100,200],
                    x.L[3],
                    x.L[1:4]
                    
                ])
                {
                    print @str(    e = ) + `e`
                    
                }
                
                print '\nDemonstration of various mutative functions'
                del x.L[0:len(x.L)]
                @for (e in 
                [
                    x.L.extend([10,20,30,40,50]),
                    x.L.append(100),
                    x.L.insert(0,-1),
                    x.L[0] = 4,
                    del x.L[1],
                    del x.L[1:3],
                    x.L[1:1] = [1,2,3,4,5],
                    x.L.remove(2),
                    x.L.pop(),
                    x.L.pop(1)
                ])
                {
                    print @str(  e)
                    e
                    print @str(    x.L = ) + `x.L`
                    
                }

                #assignment to vector not supported
                #x.L = []
                
                #deletion of vector not supported
                #del x.L
                
                #sort not supported
                #x.L.sort()

                #reverse not supported
                #x.L.reverse()

                print 'x = ' + `x`
            ));
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/*
ArraysInModels
--------------

    Arrays in models example
    {
        x = X([0,0,0,0,0,0,0],[Point(0,0),Point(0,0),Point(0,0)])
        Demonstration of various read only functions
            x.A =  = [1,2,3,2,11,5,5]
            bool(x.A) = True
            str(x.A) = ' = [1,2,3,2,11,5,5]'
            repr(x.A) = ' = [1,2,3,2,11,5,5]'
            len(x.A) = 7
            min(x.A) = 1
            max(x.A) = 11
            x.A.index(2) = 1
            x.A.index(11) = 4
            x.A.index(15) = -1
            x.A.count(5) = 2
            x.A.count(20) = 0
            x.A*2 =  = [1,2,3,2,11,5,5,1,2,3,2,11,5,5]
            x.A[-1] = 5
            3 in x.A = True
            10 in x.A = False
            3 not in x.A = False
            10 not in x.A = True
            x.A+[100,200] =  = [1,2,3,2,11,5,5,100,200]
            x.A[3] = 2
            x.A[1:4] =  = [2,3,2]

        Demonstration of various mutative functions
          x.A[0] = 4
            x.A =  = [4,2,3,2,11,5,5]
        x = X([4,2,3,2,11,5,5],[Point(0,0),Point(0,0),Point(10,0)])
    }
*/
namespace ArraysInModels
{
    $model+ Point
    {
        ceda::int32 x;
        ceda::int32 y;
    };

    $struct+ X isa ceda::IObject :
        model
        {
            ceda::int32 A[7];
            Point P[3];
        }
    {
    };

    // todo: In Python 2.7, calling 
    //      dir(x.A)
    // in Demonstration of various read only functions
    // causes a crash (but worked in Python 2.4.4)

    void Run()
    {
		ceda::TraceGroup g("Arrays in models example");

        ceda::CSpaceCreator cspace;
        ceda::CSpaceLock lock;

        PyRun_SimpleString(
            @strx
            (
                x = rootnamespace.ArraysInModels.X()
                print 'x = ' + `x`
                
                x.A[0] = 1
                x.A[1] = 2
                x.A[2] = 3
                x.A[3] = 2
                x.A[4] = 11
                x.A[5] = 5
                x.A[6] = 5
                
                x.P[2].x = 10
                
                print 'Demonstration of various read only functions'
                @for (e in 
                [
                    x.A,
                    bool(x.A),
                    str(x.A),
                    repr(x.A),
                    len(x.A),
                    min(x.A),
                    max(x.A),
                    x.A.index(2),
                    x.A.index(11),
                    x.A.index(15),
                    x.A.count(5),
                    x.A.count(20),
                    x.A*2,
                    x.A[-1],
                    3 in x.A,
                    10 in x.A,
                    3 not in x.A,
                    10 not in x.A,
                    x.A+[100,200],
                    x.A[3],
                    x.A[1:4]
                    
                ])
                {
                    print @str(    e = ) + `e`
                    
                }
                
                print '\nDemonstration of various mutative functions'
                @for (e in 
                [
                    x.A[0] = 4
                ])
                {
                    print @str(  e)
                    e
                    print @str(    x.A = ) + `x.A`
                    
                }

                # int32[7] has no member 'remove'
                # x.A.remove(2)
                
                # int32[7] has no member 'pop'
                # x.A.pop()

                # int32[7] has no member 'sort'
                #x.A.sort()

                # int32[7] has no member 'reverse'
                #x.A.reverse()

                # Deletion of slice of array int32[7] invalid
                # del x.A[1:3]

                # Assignment to slice of array int32[7] invalid
                # x.A[1:1] = [1,2,3,4,5]
                
                # Deletion of int32 variable not permitted here
                # del x.A[1]

                #assignment to array not supported
                #x.A = []
                
                #deletion of array not supported
                #del x.A

                print 'x = ' + `x`
            ));
    }
}

/*
namespace Tbu
{
    $model CvPoint2D32f
    {
        ceda::float32 x;
        ceda::float32 y;
    };
    
    $model ChessboardCornerPoints
    {
        ceda::xvector<CvPoint2D32f> Points;
    };
     
    $model ChessboardCornerPointsVector
    {
        ceda::xvector< ChessboardCornerPoints > PointSets;
    };
    
    void Run()
    {
		ceda::TraceGroup g("Tbu example");

        ceda::CSpaceCreator cspace;
        
        PyRun_SimpleString(
            @strx
            (
                cspace = ceda.GetThreadCSpace()
                cspace.Lock( 1 )

                points = rootnamespace.Tbu.ChessboardCornerPoints()
                print `points`
                print `points.Points`
                pointsets = rootnamespace.Tbu.ChessboardCornerPointsVector()
                print `pointsets`
                print `pointsets.PointSets`

                cspace.Unlock()
            ));
    }     
}
*/

///////////////////////////////////////////////////////////////////////////////////////////////////
namespace Models
{
    void Run()
    {
        Models1::Run();
        ModelConstructors::Run();
        ModelConstructors2::Run();
        VectorsInModels::Run();
        ArraysInModels::Run();
        //Tbu::Run();
    }
}