[C++-sig] better way of returning python objects

Alexis H. Rivera-Rios ahrivera at yahoo.com
Fri Jul 29 19:26:11 CEST 2005


Hi,

This is my situation.  There is a C++ library called
gmtl that has been ported to python using Pyste and
Boost.Python.
In my code, I have a C++ class that looks like this

--- question #1 ---

class Entity
{
private:
    gmtl::Point3d position;
    gmtl::Vec3d velocity;
    gmtl::Vec3d acceleration;
public:
    void SetPosition(const gmtl::Point3d& pos) ...
    gmtl::Point3d GetPosition() const ...
    void SetVelocity(const gmtl::Vec3d& vel) ...
    gmtl::Vec3d GetVelocity() const ...
    void SetAcceleration(const gmtl::Vec3d& acc) ...
    gmtl::Vec3d GetAcceleration() const ...
};


I want to be able to do this in python

import gmtl; <-- this I can do already because the
library has python bindings
from MyModule import *

t = Entity
t.SetPosition(gmtl.Point3d(1,2,3))  
y = t.GetPosition()

The only way I've figure to do this is the following:
(this is an excerpt of what pyste generated, I changed
the Get/Set methods to this)

 scope* StatATA_Entity_scope = new scope(
    class_< StatATA::Entity, boost::noncopyable,
StatATA_Entity_Wrapper >("Entity", init<  >())
	.def("SetPosition", &SetPosition)
        .def("GetPosition", &GetPosition)
    );

Where,

void SetPosition(  StatATA::Entity &e, const object
&pos )
{
	gmtl::Point3d
p(extract<double>(pos.attr("__getitem__")(0)),
					extract<double>(pos.attr("__getitem__")(1)),
					extract<double>(pos.attr("__getitem__")(2)));
	e.SetPosition(p);	
}

object GetPosition(  StatATA::Entity &e )
{
	object main_module((
   
handle<>(borrowed(PyImport_AddModule("__main__")))));
	object main_namespace = main_module.attr("__dict__");

	object result((handle<>(
    PyRun_String("gmtl.Point3d()"
        , Py_eval_input
        , main_namespace.ptr()
        , main_namespace.ptr()))
	));
	gmtl::Point3d t (e.GetPosition());
	result[0] = t[0];
	result[1] = t[1];
	result[2] = t[2];
	return result;
}

This works. But, is there a better way?

---- question #2 ---

Now, my entity has additional member variables with
other gmtl types.  
It seems that I have to repeat the same process with
each type.

I want to generalize that function so I can specify
the object function to call and the destination type:

class_< StatATA::Entity, boost::noncopyable,
StatATA_Entity_Wrapper >("Entity", init<  >())
	.def("SetPosition",
&GenericSet<Entity::SetPosition,gmtl::Point3d>)
	.def("SetVelocity",
&GenericSet<Entity::SetVelocity,gmtl::Vec3d>)
    );

The closest I've got to do that is this class:

template <class Object>
struct GenSet
{
	typedef void (Object::*MemberFunc) (const
gmtl::Point3d&) ;
	MemberFunc f;

	GenSet(MemberFunc x)
	{
		f = x;
	};
	void operator()(Object &obj, const object &pos)
	{
		gmtl::Point3d
p(extract<double>(pos.attr("__getitem__")(0)),
					extract<double>(pos.attr("__getitem__")(1)),
					extract<double>(pos.attr("__getitem__")(2)));
		(obj.*f)(p);	
	}

};


So in theory I can do the following:

GenSet<Entity> xxx(&Entity::SetPosition)
xxx(myobject, bla)

But I run into the following problems when I try to
use this in the code:

1) declaring an object without the variable name gives
a syntax error:

GenSet<Entity> (&Entity::SetPosition)

c:\Development\PyStatATA\pystatata.cpp(119): 
error C2761: 'void StatATA::Entity::SetPosition(const
gmtl::Point3d &)' : member function redeclaration not
allowed

Why does that mean?

2) Passing the class to the .def function doesn't
compile either:

GenSet<Entity> xxx(&Entity::SetPosition)
class_< StatATA::Entity, boost::noncopyable,
StatATA_Entity_Wrapper >("Entity", init<  >())
	.def("SetPosition", &xxx)

c:\Boost\include\boost-1_32\boost\python\class.hpp(500)
: error C2784:
'boost::mpl::vector17<RT,most_derived<Target,ClassT>::type&,T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14>
boost::python::detail::get_signature(RT (__thiscall
ClassT::*
)(T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14)
volatile const,Target *)' : could not deduce template
argument for 'T2 (__thiscall T3::*
)(T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,T17,T18)
volatile const' from
'`anonymous-namespace'::GenSet<Object>  with
[Object=StatATA::Entity]';
c:\Boost\include\boost-1_32\boost\python\signature.hpp(164)
: see declaration of
'boost::python::detail::get_signature';
c:\Boost\include\boost-1_32\boost\python\class.hpp(237)
: see reference to function template instantiation
'void
boost::python::class_<W,X1,X2>::def_impl<W,F,boost::python::detail::def_helper<T1>>(T
*,const char *,Fn,const Helper &,...) with
[W=StatATA::Entity,X1=boost::noncopyable,X2=`anonymous-namespace'::StatATA_Entity_Wrapper,F=`anonymous-namespace'::GenSet<StatATA::Entity>
*__w64 ,T1=const char
*,T=StatATA::Entity,Fn=`anonymous-namespace'::GenSet<StatATA::Entity>
*__w64 ,Helper=boost::python::detail::def_helper<const
char *>]' being compiled;
c:\Development\PyStatATA\pystatata.cpp(140) : see
reference to function template instantiation
'boost::python::class_<W,X1,X2>::self
&boost::python::class_<W,X1,X2>::def<`anonymous-namespace'::GenSet<Object>*__w64
>(const char *,F) with
[W=StatATA::Entity,X1=boost::noncopyable,X2=`anonymous-namespace'::StatATA_Entity_Wrapper,Object=StatATA::Entity,F=`anonymous-namespace'::GenSet<StatATA::Entity>
*__w64 ]' being compiled
c:\Boost\include\boost-1_32\boost\python\class.hpp(500)
: error C2780:
'boost::mpl::vector17<RT,ClassT&,T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14>
boost::python::detail::get_signature(RT (__thiscall
ClassT::*
)(T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14)
volatile const)' : expects 1 arguments - 2 provided;
c:\Boost\include\boost-1_32\boost\python\signature.hpp(146)
: see declaration of
'boost::python::detail::get_signature'
c:\Boost\include\boost-1_32\boost\python\class.hpp(500)
: error C2784:
'boost::mpl::vector17<RT,most_derived<Target,ClassT>::type&,T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14>
boost::python::detail::get_signature(RT (__thiscall
ClassT::*
)(T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14)
volatile,Target *)' : could not deduce template
argument for 'T2 (__thiscall T3::*
)(T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,T17,T18)
volatile' from '`anonymous-namespace'::GenSet<Object> 
with [Object=StatATA::Entity]';
c:\Boost\include\boost-1_32\boost\python\signature.hpp(164)
: see declaration of
'boost::python::detail::get_signature'
c:\Boost\include\boost-1_32\boost\python\class.hpp(500)
: error C2780:
'boost::mpl::vector17<RT,ClassT&,T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14>
boost::python::detail::get_signature(RT (__thiscall
ClassT::*
)(T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14)
volatile)' : expects 1 arguments - 2 provided;
c:\Boost\include\boost-1_32\boost\python\signature.hpp(146)
: see declaration of
'boost::python::detail::get_signature'
... 

So I guess it means we can't pass functors to the .def
functions?


In summary, 

- Is there a better way to convert a c++ object to its
respective python object without me having to 
expose the type since that has been done for me?
- Assuming that my solution for the previous question
is acceptable,
how can I generatize my setposition function so its
parameterized on the gmtl type, and object function in
a way that works
with the .def function?

Any comments or feedback will be greatly appreciated,

Alexis








Programming Tutorial:
In Python: To do this, do this
In Perl: To do this, do this or this or this or this...
In C: To do this, do this, but be careful
In C++: To do this, do this, but don't do this, be careful of this, watch out for this, and whatever you do, don't do this


		
____________________________________________________
Start your day with Yahoo! - make it your home page 
http://www.yahoo.com/r/hs 
 



More information about the Cplusplus-sig mailing list