Pointers.cpp

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

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

///////////////////////////////////////////////////////////////////////////////////////////////////
/*
Pointers
--------

Pointer values are wrapped in PyObjects,  allowing pointers to be passed around from within 
Python.

Type checking is performed at run time.  In the example below the int32* return value cannot be
passed into a function taking a float64*.
*/

namespace Pointers1
{
    $function+ ceda::int32* ReturnPtr()
    {
        static ceda::int32 x = 10;
        return &x;
    }

    $function+ void PassPtr(ceda::int32* x)
    {
        Tracer() << "PassPtr : *x = " << *x << '\n';
    }

    $function+ void PassFloat64Ptr(ceda::float64* x)
    {
    }
	
	void Run()
	{
		ceda::TraceGroup g("Pointers example 1");
		
		Tracer() << "ReturnPtr() = " << ReturnPtr() << "\n\n";
		
        PyRun_SimpleString(
            @strx
            (
                ns = rootnamespace.Pointers1
                
                # The pointer return value is wrapped in a PyObject.
                p = ns.ReturnPtr()

                # prints the value of the pointer.  Eg '004262F4' on 32 bit platform
                print 'p = ' + `p`
                
                # Pointers of a compatible type can be passed into functions
                ns.PassPtr( p )

                # Causes TypeError at run time : Cannot assign float64* from int32*
                try:
                    ns.PassFloat64Ptr( p )
                except TypeError as e:
                    print("Caught " + type(e).__name__ + ": " + str(e))
            ));
	}	
}


///////////////////////////////////////////////////////////////////////////////////////////////////
/*
Within Python a pointer to a signed integer can be reinterpreted as a pointer to an unsigned 
integer of the same size and vice versa.
*/

namespace Pointers2
{
    $function+ ceda::int32* ReturnPtr()
    {
        static ceda::int32 x = -1;
        return &x;
    }

    $function+ void PassPtr(ceda::uint32* x)
    {
        Tracer() << "PassPtr : *x = " << *x << '\n';
    }
	
	void Run()
	{
		ceda::TraceGroup g("Pointers to signed/unsigned integers");
		
        PyRun_SimpleString(
            @strx
            (
                ns = rootnamespace.Pointers2
                ns.PassPtr( ns.ReturnPtr() )
            ));
	}	
}


///////////////////////////////////////////////////////////////////////////////////////////////////
/*
Null Pointers are always represented using Py_None.  

Note therefore that null pointers carry no type information.  In the example below, Python happily
allow the null int8* to be passed into a function taking a float64*
*/

namespace Pointers3
{
    $function+ ceda::int8* ReturnNull()
    {
        return NULL;
    }

    $function+ void PassPtr(ceda::float64* p)
    {
        Tracer() << "PassPtr : p = " << (void*) p << '\n';
    }
	
	void Run()
	{
		ceda::TraceGroup g("Null pointers");
		
        PyRun_SimpleString(
            @strx
            (
                ns = rootnamespace.Pointers3
                
                p = ns.ReturnNull()
                
                if p:
                    print 'p is not null'
                else:
                    print 'p is null'
                    
                ns.PassPtr( p )
                ns.PassPtr( None )
            ));
	}	
}


///////////////////////////////////////////////////////////////////////////////////////////////////
/*
Class pointers
--------------

A reflected class may be returned by value or pointer without having any impact on the syntax
used for accessing reflected member variables or methods or accessing implemented interfaces.
*/

namespace Pointers4
{
    $struct+ X
    {
        X(ceda::int32 v=0) : val(v) {}
        
        $ceda::int32 val;
    };

    $function+ X ReturnX()
    {
        return X(100);
    }

    $function+ X* ReturnXPtr()
    {
        static X x(200);
        return &x;
    }
    
    $function+ void IncVal(X* px)
    {
        ++px->val;
    }

	void Run()
	{
		ceda::TraceGroup g("Class/struct pointers");
		
        PyRun_SimpleString(
            @strx
            (
                ns = rootnamespace.Pointers4
                
                @def m(x) =
                {
                    print @str(x.val = ) + `x.val`
                    x.val = x.val + 10
                    ns.IncVal(x)
                    print @str(x.val = ) + `x.val`
                }
                
                x1 = ns.ReturnX()
                m(x1)

                x2 = ns.ReturnXPtr()
                m(x2)
            ));
	}	
}


///////////////////////////////////////////////////////////////////////////////////////////////////
/*
References
----------

Generally speaking references are treated like pointers.  The main difference is that Py_None
cannot be passed to a function taking a reference.  This error is trapped at run time.
*/

namespace Pointers5
{
    $function+ ceda::int32& ReturnRef()
    {
        static ceda::int32 x = 10;
        return x;
    }

    $function+ void Inc(ceda::int32& x)
    {
        ++x;
    }

	void Run()
	{
		ceda::TraceGroup g("References");
		
        PyRun_SimpleString(
            @strx
            (
                ns = rootnamespace.Pointers5
                ns.Inc(ns.ReturnRef())
                
                # TypeError : Cant initialise reference int32& from None
                # ns.Inc(None)
            ));
            
        Tracer() << "x = " << ReturnRef() << '\n';
	}	
}


///////////////////////////////////////////////////////////////////////////////////////////////////
/*
Implicit indirections
---------------------

Indirections are implicitly performed on pointers in order to coerce into references or values

Output:

    Return Val          Pass Val      : 1
    Return Ptr          Pass Val      : 2
    Return Ref          Pass Val      : 3
    Return Const Val    Pass Val      : 4
    Return Const Ptr    Pass Val      : 5
    Return Const Ref    Pass Val      : 6
    Return Val          Pass ConstVal : 1
    Return Ptr          Pass ConstVal : 2
    Return Ref          Pass ConstVal : 3
    Return Const Val    Pass ConstVal : 4
    Return Const Ptr    Pass ConstVal : 5
    Return Const Ref    Pass ConstVal : 6
    Return Val          Pass ConstPtr : 1
    Return Ptr          Pass ConstPtr : 2
    Return Ref          Pass ConstPtr : 3
    Return Const Val    Pass ConstPtr : 4
    Return Const Ptr    Pass ConstPtr : 5
    Return Const Ref    Pass ConstPtr : 6
    Return Val          Pass ConstRef : 1
    Return Ptr          Pass ConstRef : 2
    Return Ref          Pass ConstRef : 3
    Return Const Val    Pass ConstRef : 4
    Return Const Ptr    Pass ConstRef : 5
    Return Const Ref    Pass ConstRef : 6
    Return Ptr          Pass Ptr      : 2
    Return Ref          Pass Ptr      : 3
    Return Ptr          Pass Ref      : 2
    Return Ref          Pass Ref      : 3
*/

namespace Pointers6
{
    $function+ void PassVal(ceda::int32 v)
    {
        Tracer() << "Pass Val      : " << v << '\n';
    }

    $function+ void PassPtr(ceda::int32* v)
    {
        Tracer() << "Pass Ptr      : " << *v << '\n';
    }

    $function+ void PassRef(ceda::int32& v)
    {
        Tracer() << "Pass Ref      : " << v << '\n';
    }

    $function+ void PassConstVal(const ceda::int32 v)
    {
        Tracer() << "Pass ConstVal : " << v << '\n';
    }

    $function+ void PassConstPtr(const ceda::int32* v)
    {
        Tracer() << "Pass ConstPtr : " << *v << '\n';
    }

    $function+ void PassConstRef(const ceda::int32& v)
    {
        Tracer() << "Pass ConstRef : " << v << '\n';
    }

    $function+ ceda::int32 ReturnVal()
    {
        Tracer() << "Return Val      ";
        return 1;
    }

    $function+ ceda::int32* ReturnPtr()
    {
        Tracer() << "Return Ptr      ";
        static ceda::int32 s = 2;
        return &s;
    }

    $function+ ceda::int32& ReturnRef()
    {
        Tracer() << "Return Ref      ";
        static ceda::int32 s = 3;
        return s;
    }

    $function+ const ceda::int32 ReturnConstVal()
    {
        Tracer() << "Return Const Val";
        return 4;
    }

    $function+ const ceda::int32* ReturnConstPtr()
    {
        Tracer() << "Return Const Ptr";
        static ceda::int32 s = 5;
        return &s;
    }

    $function+ const ceda::int32& ReturnConstRef()
    {
        Tracer() << "Return Const Ref";
        static ceda::int32 s = 6;
        return s;
    }

	void Run()
	{
		ceda::TraceGroup g("Implicit indirections");
		
        PyRun_SimpleString(
            @strx
            (
                ns = rootnamespace.Pointers6
                
                ns.PassVal(ns.ReturnVal())
                ns.PassVal(ns.ReturnPtr())
                ns.PassVal(ns.ReturnRef())
                ns.PassVal(ns.ReturnConstVal())
                ns.PassVal(ns.ReturnConstPtr())
                ns.PassVal(ns.ReturnConstRef())

                ns.PassConstVal(ns.ReturnVal())
                ns.PassConstVal(ns.ReturnPtr())
                ns.PassConstVal(ns.ReturnRef())
                ns.PassConstVal(ns.ReturnConstVal())
                ns.PassConstVal(ns.ReturnConstPtr())
                ns.PassConstVal(ns.ReturnConstRef())

                ns.PassConstPtr(ns.ReturnVal())
                ns.PassConstPtr(ns.ReturnPtr())
                ns.PassConstPtr(ns.ReturnRef())
                ns.PassConstPtr(ns.ReturnConstVal())
                ns.PassConstPtr(ns.ReturnConstPtr())
                ns.PassConstPtr(ns.ReturnConstRef())

                ns.PassConstRef(ns.ReturnVal())
                ns.PassConstRef(ns.ReturnPtr())
                ns.PassConstRef(ns.ReturnRef())
                ns.PassConstRef(ns.ReturnConstVal())
                ns.PassConstRef(ns.ReturnConstPtr())
                ns.PassConstRef(ns.ReturnConstRef())

                ns.PassPtr(ns.ReturnPtr())
                ns.PassPtr(ns.ReturnRef())
                ns.PassRef(ns.ReturnPtr())
                ns.PassRef(ns.ReturnRef())

                # Error.  Const cast
                #ns.PassPtr(ns.ReturnConstPtr())
                #ns.PassRef(ns.ReturnConstPtr())
                #ns.PassPtr(ns.ReturnConstRef())
                #ns.PassRef(ns.ReturnConstRef())

                # Error.  Cannot convert from value to mutable pointer
                #ns.PassPtr(ns.ReturnVal())
                #ns.PassPtr(ns.ReturnConstVal())
                #ns.PassRef(ns.ReturnVal())
                #ns.PassRef(ns.ReturnConstVal())
            ));
	}	
}


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

namespace Pointers
{
    void Run()
    {
        Pointers1::Run();
        Pointers2::Run();
        Pointers3::Run();
        Pointers4::Run();
        Pointers5::Run();
        Pointers6::Run();
    }
}