return_range

Definition

Class return_range is a model of CallPolicies, which can be used to wrap C++ functions that return a pointer to some array. The new call policy constructs object, which provides a regular Python sequence interface.

Example

struct image_t{

    ...

    const unsigned char* get_data() const{
        return m_raw_data;
    }

    ssize_t get_width() const{
        return m_width;
    }

    ssize_t get_height() const{
        return m_height;
    }

private:
    unsigned long m_width;
    unsigned long m_height;
    unsigned char* m_raw_data;
};

Before introducing the whole solution, I would like to describe “return_range” interface.

return_range class

template < typename TGetSize
           , typename TValueType
           , typename TValuePolicies=boost::python::default_call_policies >
struct return_range : boost::python::default_call_policies
{ ... };

Boost.Python call policies are stateless classes, which do not care any information about the invoked function or object. In our case we have to pass the following information:

  • the array size
  • the array type
  • “__getitem__” call policies for the array elements

TGetSize parameter

TGetSize is a class, which is responsible to find out the size of the returned array.

TGetSize class must have:

  • default constructor

  • call operator with the following signature:

    ssize_t operator()( boost::python::tuple args );
    

    args is a tuple of arguments, the function was called with.

    Pay attention: this operator will be invoked after the function. This call policy is not thread-safe!

For our case, the following class could be defined:

struct image_data_size_t{
    ssize_t operator()( boost::python::tuple args ){
        namespace bpl = boost::python;
        bpl::object self = args[0];
        image_t& img = bpl::extract< image_t& >( self );
        return img.get_width() * img.get_height();
    }
};

Passing all arguments, instead of single “self” argument gives you an ability to treat functions, where the user asked to get access to the part of the array.

struct image_t{
    ...
    const unsigned char* get_data(ssize_t offset) const{
        //check that offset represents a legal value
        ...
        return &m_raw_data[offset];
    }
    ...
};

The following “get size” class treats this situation:

struct image_data_size_t{
    ssize_t operator()( boost::python::tuple args ){
        namespace bpl = boost::python;
        bpl::object self = args[0];
        image_t& img = bpl::extract< image_t& >( self );
        bpl::object offset_obj = args[1];
        ssize_t offset = bpl::extract< ssize_t >( offset_obj );
        return img.get_width() * img.get_height() - offset;
    }
};

TValueType parameter

TValueType is a type of array element. In our case it is unsigned char.

TValuePolicies parameter

TValuePolicies is a “call policy” class, which will be applied when the array element is returned to Python. This is a call policy for “__getitem__” function.

unsigned char is mapped to immutable type in Python, so I have to use default_call_policies. default_call_policies is a default value for TValuePolicies parameter.

I think, now you are ready to see the whole solution:

namespace bpl = boost::python;
namespace ppc = pyplusplus::call_policies;

BOOST_PYTHON_MODULE(my_module){
  bpl::class_< image_t >( "image_t" )
      .def( "get_width", &image_t::get_width )
      .def( "get_height", &image_t::get_height )
      .def( "get_raw_data", ppc::return_range< image_size_t, unsigned char >() );
}

Py++ integration

The Py++ code is not that different from what you already know:

from pyplusplus import module_builder
from pyplusplus.module_builder import call_policies

image_size_code = \
"""
struct image_data_size_t{
    ssize_t operator()( boost::python::tuple args ){
        namespace bpl = boost::python;
        bpl::object self = args[0];
        image_t& img = bpl::extract< image_t& >( self );
        return img.get_width() * img.get_height();
    }
};
"""

mb = module_builder.module_builder_t( ... )
image = mb.class_( 'image_t' )
image.add_declaration_code( image_size_code )
get_raw_data = image.mem_fun( 'get_raw_data' )
get_raw_data.call_policies \
    = call_policies.return_range( get_raw_data, "image_data_size_t" )

call_policies.return_range arguments:

  1. A reference to function. Py++ will extract by itself the type of the array element.
  2. A name of “get size” class.
  3. A call policies for “__getitem__” function. Py++ will analyze the array element type. If the type is mapped to immutable type, than default_call_policies is used, otherwise you have to specify call policies.

Python usage code:

from my_module import *

img = image_t(...)
for p in img.get_raw_data():
    print p

Dependencies

The new call policy depends on new indexing suite and Py++ :-).