Boost.Python has a nice introduction to call policies. "Call policies concept" document will provide you with formal definition.
The call policies in Py++ are named exactly as in Boost.Python, only the syntax is slightly different. For instance, this call policy:
return_internal_reference< 1, with_custodian_and_ward<1, 2> >()
becomes in Py++
return_internal_reference( 1, with_custodian_and_ward(1, 2) )
Py++ supports all call policies presented in Boost.Python.
Every "callable" object in Py++ has call_policies property.
C++ code:
struct data{...}; const data& do_smth( const data& d, int x ); void return_second_arg( int x, int y ); typedef struct opaque_ *opaque_pointer; opaque_pointer get_opaque();
Python code:
from pyplusplus import module_builder from pyplusplus.module_builder import call_policies mb = module_builder.module_builder_t( ... ) mb.free_function( 'return_second_arg' ).call_policies = call_policies.return_arg( 2 ) #---------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mb.member_function( 'do_smth' ).call_policies = call_policies.return_self() #-------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mb.calldef( 'get_opaque' ).call_policies = call_policies.return_value_policy( call_policies.return_opaque_pointer )
Py++ is able to "guess" few call policies, base on analysis of return type and\or operator name:
default_call_policies:
return_value_policy
return_opaque_pointer
return type is void*
return type is const void*
return type is T* and T is a user defined opaque type
class_t and class_declaration_t classes have opaque property. You have to set it to True, if you want Py++ to create this call policy automatically for all functions, that use T* as return type.
copy_const_reference
return_by_value
copy_non_const_reference
return_internal_reference
return_self
This call policy will be used for operator=.
If you don't specify call policy for a function and it needs one, few things will happen:
Before you read this paragraph consider to read Boost.Python return_opaque_pointer documentation.
return_value_policy( return_opaque_pointer ) is a special policy for Boost.Python. In this case, it requires from you to define specialization for the boost::python::type_id function on the type pointed to by returned pointer. Py++ will generate the required code.
Actually you should define boost::python::type_id specialization also in case a function takes the opaque type as an argument. Py++ can do it for you, all you need is to say to mark a declaration as opaque.
Example:
struct identity_impl_t{};
typedef identity_impl_t* identity;
struct world_t{
world_t( identity id );
identity get_id() const;
...
};
Py++ code:
mb = module_builder_t(...)
mb.class_( 'identity_impl_t' ).opaque = True
custom_call_policies policies functionality was born to allow you to define your own call polices and use them with same level of convenience as built-in ones.
The usage is pretty simple:
from pyplusplus import module_builder
from pyplusplus.module_builder import call_policies
mb = module_builder.module_builder_t( ... )
mb.free_function( ... ).call_policies \
= call_policies.custom_call_policies( your call policies code )
Optionally you can specify name of the header file, which should be included:
mb.free_function( ... ).call_policies \
= call_policies.custom_call_policies( your call policies code, "xyz.hpp" )
Class return_pointee_value is a model of ResultConverterGenerator which can be used to wrap C++ functions returning any pointer, such that the pointee of return value is copied into a new Python object.
float* get_value(){
static float value = 0.5;
return &value;
}
float* get_null_value(){
return (float*)( 0 );
}
namespace bpl = boost::python;
BOOST_PYTHON_MODULE(my_module){
def( "get_value"
, bpl::return_value_policy< pyplusplus::call_policies::return_pointee_value<> >() );
def( "get_null_value"
, bpl::return_value_policy< pyplusplus::call_policies::return_pointee_value<> >() );
}
The Py++ code is not that different from what you already know:
from pyplusplus import module_builder
from pyplusplus.module_builder import call_policies
mb = module_builder.module_builder_t( ... )
mb.free_function( return_type='float *' ).call_policies \
= call_policies.return_value_policy( call_policies.return_pointee_value )
Python code:
import my_module
assert 0.5 == my_module.get_value()
assert None is my_module.get_null_value()
Class as_tuple is a model of ResultConverterGenerator which can be used to wrap C++ functions returning a pointer to arrays with fixed size. The policy will construct a Python tuple from the array and treat the array memory.
struct vector3{
...
float* clone_raw_data() const{
float* values = new float[3];
//copy values
return values;
}
const flow* get_raw_data() const{
return m_values;
}
private:
float m_values[3];
};
namespace bpl = boost::python;
namespace pypp_cp = pyplusplus::call_policies;
BOOST_PYTHON_MODULE(my_module){
bpl::class_< vector3 >( "vector3" )
.def( "clone_raw_data"
, &::vector3::clone_raw_data
, bpl::return_value_policy< pypp_cp::arrays::as_tuple< 3, pypp_cp::memory_managers::delete_ > >() )
.def( "get_raw_data"
, &::vector3::get_raw_data
, bpl::return_value_policy< pypp_cp::arrays::as_tuple< 3, pypp_cp::memory_managers::none > >() ) );
}
as_tuple is a template class that takes few arguments:
The Py++ code is slightly different from the C++ one, but it is definitely shorter:
from pyplusplus import module_builder
from pyplusplus.module_builder import call_policies
mb = module_builder.module_builder_t( ... )
mb.member_function( 'clone_raw_data' ).call_policies \
= call_policies.convert_array_to_tuple( 3, call_policies.memory_managers.delete_ )
mb.member_function( 'get_raw_data' ).call_policies \
= call_policies.convert_array_to_tuple( 3, call_policies.memory_managers.none )
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.
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.
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 out case we have to pass next information:
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 next 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, next 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];
}
...
};
Next "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 is a type of array element. In our case it is unsigned char.
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 >() );
}
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:
Python usage code:
from my_module import *
img = image_t(...)
for p in img.get_raw_data():
print p
The new call policy depends on new indexing suite and Py++ :-). But if you want you can extract the relevant piece of code from this file.