Python bindings - pointers

Example

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*.

With the following C++


// C++ code
namespace ns
{
    $function+ int32* ReturnPtr()
    {
        static int32 x = 10;
        return &x;
    }

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

    $function+ void PassFloat64Ptr(float64* x)
    {
    }
}

the following Python


# python code

# 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*
ns.PassFloat64Ptr( p )

produces the following output


p = 0x00007FF757F58F64
PassPtr : *x = 10
Caught TypeError: Cannot assign float64* from int32*

Pointer to signed versus pointer to unsigned

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.

With the following C++


// C++ code
namespace ns
{
    $function+ int32* ReturnPtr()
    {
        static int32 x = -1;
        return &x;
    }

    $function+ void PassPtr(uint32* x)
    {
        std::cout << "PassPtr : *x = " << *x << '\n';
    }
}

the following Python


# python code

ns.PassPtr( ns.ReturnPtr() )

produces the following output


PassPtr : *x = 4294967295

Null pointers

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*

With the following C++


// C++ code
namespace ns
{
    $function+ int8* ReturnNull()
    {
        return NULL;
    }

    $function+ void PassPtr(float64* p)
    {
        std::cout << "PassPtr : p = " << (void*) p << '\n';
    }
}

the following Python


# python code

p = ns.ReturnNull()
                
if p:
    print 'p is not null'
else:
    print 'p is null'
                    
ns.PassPtr( p )
ns.PassPtr( None )

produces the following output


p is null
PassPtr : p = 0000000000000000
PassPtr : p = 0000000000000000

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.

With the following C++


// C++ code
namespace ns
{
    $struct+ X
    {
        X(int32 v=0) : val(v) {}
        $int32 val;
    };

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

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

the following Python


# python code

x1 = ns.ReturnX()
print 'x1.val = ' + `x1.val`
x1.val = x1.val + 10
ns.IncVal(x1)
print 'x1.val = ' + `x1.val`

x2 = ns.ReturnX()
print 'x2.val = ' + `x2.val`
x2.val = x2.val + 10
ns.IncVal(x2)
print 'x2.val = ' + `x2.val`

produces the following output


x1.val = 100
x1.val = 111
x2.val = 200
x2.val = 211

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.

With the following C++


// C++ code
namespace ns
{
    $function+ int32& ReturnRef()
    {
        static int32 x = 10;
        return x;
    }

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

the following Python


# python code

ns.Inc(ns.ReturnRef())
                
# TypeError : Can't initialise reference int32& from None
# ns.Inc(None)

Implicit indirections

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

With the following C++


// C++ code
namespace ns
{
    $function+ void PassVal(int32 v)
    {
        std::cout << "Pass Val      : " << v << '\n';
    }

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

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

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

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

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

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

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

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

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

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

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

the following Python


# python code

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())

produces the following 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