GlobalFunctions.cpp

// GlobalFunctions.cpp
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2007

#include "Ceda/cxUtils/TracerUtils.h"
@import "Ceda/cxPython/cxPython.h"


///////////////////////////////////////////////////////////////////////////////////////////////////
/*
Global functions
----------------

Python may call reflected global functions, accessed in the namespace hierarchy under the root 
ceda namespace (rootnamespace).
*/

namespace GlobalFunctions1
{
	$function+ ceda::int32 sum(ceda::int32 x, ceda::int32 y)
	{
	    return x + y;
	}
	
	void Run()
	{
		ceda::TraceGroup g("Global functions example 1");

        int ret = PyRun_SimpleString(
            @strx
            (
                print 'sum(1,2*2) = ' + `rootnamespace.GlobalFunctions1.sum(1,2*2)`
            ));
        cxAssert(ret == 0);
	}	
}


///////////////////////////////////////////////////////////////////////////////////////////////////
/*
Out parameters
--------------

A curious fact about C++
------------------------

Consider the following C++ code:

    int b();
    void f(int& x);
    
    // error C2664: 'f' : cannot convert parameter 1 from 'int' to 'int &'
    f(b());

f(b()) is not permitted because f() modifies a temporary.

Now consider this C++ code:

    string8 b();
    void f(string8& x);
    
    // compiles!
    f(b());

It seems that classes are treated differently to basic types in this respect.  One would have
hoped that neither compiles.  This appears to be a case of C++ not supporting value types 
properly.  The fact that b() returns by value demonstrates that a value type is involved and 
it makes no sense to pass a value to a function that looks like it treats it as an out 
parameter.

In the python bindings we take the view that the address of temporaries cannot be passed into
functions that (by their signature) appear to treat the parameter as an out parameter.

Notes on example
----------------

In this example bar() returns a string by value.  foo() takes a string by reference.
In python the call foo(bar()) is not permitted because it is assumed the string passed to 
foo() can be modified so it doesn't make sense to pass a value.

In order to call foo() we need to pass a *variable* holding a string.  One way this could be 
done is write a function MakeString() that allocates a string variable on the heap.
*/

namespace OutParams1
{
    $function+ ceda::string8 bar() { return "hi"; }
    
    // foo() has an out parameter, which is the subject of this example
    $function+ void foo(ceda::string8& x) { x = "hello"; }

    $function+ ceda::string8* MakeString()
    {
        return new ceda::string8;
    }
    $function+ ceda::string8 CopyAndDeleteString(ceda::string8* x)
    {
        ceda::string8 y;
        y.swap(*x);
        delete x;
        return y;
    }
    
    void Run()
    {
        @if (@str(@platform) == "Win64")
        {
            foo(bar());     // Compiles in VC2012 even though it makes no sense
        }

        ceda::TraceGroup g("OutParams example 1");
    
        PyRun_SimpleString(
            @strx
            (
                ns = rootnamespace.OutParams1

                # Error: Cannot assign string8* from non pointer type 
                # print `ns.foo(ns.bar())`
                
                x = ns.MakeString()
                ns.foo(x)
                y = ns.CopyAndDeleteString(x)
                print 'y = ' + `y`
            ));
	}	
}

namespace OutParams2
{
	$model+ X { ceda::int32 v; };
	$function+ void foo(X& x) { x.v = 10; }
	
	void Run()
	{
		ceda::TraceGroup g("OutParams example 2");
		
        PyRun_SimpleString(
            @strx
            (
                ns = rootnamespace.OutParams2
                
                x = ns.X()
                ns.foo(x)
                print 'x = ' + `x`
            ));
	}
}

namespace OutParams3
{
	// Example of function with in-out parameter
	$function+ void AddWorld(ceda::string8& x) { x += ",world"; }
	
	void Run()
	{
		ceda::TraceGroup g("OutParams example 3");
		
        PyRun_SimpleString(
            @strx
            (
                ns = rootnamespace.OutParams3
                
                print 'ceda.string8 = ' + `ceda.string8`

                # Allocates a string on the heap. x is like a pointer to a string
                x = ceda.string8("initial value")  
                print 'x = ' + `x`  # prints the address of the string
                print 'x.val = ' + `x.val`
                
                # Read/write the value of the string
                x.val = "hello"
                print 'x.val = ' + `x.val`

                # x can be passed to functions that take a string by value, pointer or reference
                ns.AddWorld(x)
                print 'x.val = ' + `x.val`
            ));
	}	
}

namespace OutParams4
{
	$function+ ceda::int32 Square1(ceda::int32 x) { return x*x; }
	$function+ ceda::int32 Square2(const ceda::int32& x) { return x*x; }
	$function+ ceda::int32 Square3(const ceda::int32* x) { return (*x)*(*x); }
	
	$function+ void Inc1(ceda::int32& x) { ++x; }
	$function+ void Inc2(ceda::int32* x) { ++(*x); }
	
	void Run()
	{
		ceda::TraceGroup g("OutParams example 4");
		
        PyRun_SimpleString(
            @strx
            (
                ns = rootnamespace.OutParams4
                
                # Allocates a string on the heap. x is like a pointer to an int32
                x = ceda.int32(5)  
                print 'x = ' + `x`  # prints the address of the int32

                # Read the value of the int32
                print 'x.val = ' + `x.val`
                
                # Write the value of the int32
                x.val = 10

                # Read the value of the int32
                print 'x.val = ' + `x.val`

                # x can be passed to functions that take an int32 by pointer or reference
                ns.Inc1(x)
                ns.Inc2(x)
                print 'x.val = ' + `x.val`
                
                # x can be passed to functions that take an int32 by value, const pointer or const reference
                print 'Square1(x) = ' + `ns.Square1(x)`
                print 'Square2(x) = ' + `ns.Square2(x)`
                print 'Square3(x) = ' + `ns.Square3(x)`
            ));
	}	
}

/*
It is proposed that cxCedaScript define the following function:

A self contained, typed variable

    struct TypedVar
    {
        xvector<octet_t> m_byteCode;
        xvector<xstring> m_stringTable;
        ReflectedByteCode m_rbc;            // Points at the byte code and string table
        void* m_data;                       // The variable
    };

    Parse str as a variable.  Eg
    
        int32 10
        string8 "Hello"
        int32[5] [1,4,2,3,7]
        float64[2][3] [[1,7,3],[2,5,0]]
        xvector<int32> [1,2,3]
        mydatasrc { name = "x", shapes = [ circle((0,0),10), triangle((1,2),(10,20),(15,4)) ] }

    bool ParseTypedVarFromString(SubString str, string8& error, TypedVar& tv)
    {
    }
*/

// TODO: ceda.Make doesn't seem to exist. This might be work in progress.

#if 0
namespace OutParams5
{
	void Run()
	{
		ceda::TraceGroup g("OutParams example 5");
		
        PyRun_SimpleString(
            @strx
            (
                #ns = rootnamespace.OutParams5
                
                x = ceda.Make("xvector<int32> [6,17]")   # todo: need CedaScript!
                #x = ceda.xvector(ceda.int32)()
                print 'x = ' + `x`
                
                x = x + [1,2,3]
                print 'x = ' + `x`
            ));
	}	
}
#endif



///////////////////////////////////////////////////////////////////////////////////////////////////

namespace GlobalFunctions
{
    void Run()
    {
        GlobalFunctions1::Run();
        OutParams1::Run();
        OutParams2::Run();
        OutParams3::Run();
        OutParams4::Run();
        //OutParams5::Run();
    }
}