Extracting C++ objects of different types from a Python list
I have run into a problem that I don't know how to solve with Boost.python, and I can't find the answer online. I have a list of objects in Python, and the objects are of different classes. All the classes are derived from a common base class, and the classes are defined in C++ and exposed to Python. The list of objects is created in Python, and I would like to get this list into C++, where the the objects of different types will be handled differently. I can bring the list into C++ as a boost::python::list, but then I have to use extract <type> to get the individual objects from the list. The problem is that extract<baseclass> works, but then all of the extracted objects are the same type. The information contained in the derived types is lost. Is it possible to do this with boost.python? Should I be thinking of a different way to design the code so that I can pass in a list of objects from a single class? Sincerely, Craig Finch -------------- Please reply to cfinch@ieee.org ____________________________________________________________________________________ Sick sense of humor? Visit Yahoo! TV's Comedy with an Edge to see what's on, when. http://tv.yahoo.com/collections/222
On 6/27/07, Craig Finch <oanjao@yahoo.com> wrote:
I have run into a problem that I don't know how to solve with Boost.python, and I can't find the answer online. I have a list of objects in Python, and the objects are of different classes. All the classes are derived from a common base class, and the classes are defined in C++ and exposed to Python. The list of objects is created in Python, and I would like to get this list into C++, where the the objects of different types will be handled differently. I can bring the list into C++ as a boost::python::list, but then I have to use extract <type> to get the individual objects from the list. The problem is that extract<baseclass> works, but then all of the extracted objects are the same type. The information contained in the derived types is lost.
Try to extract "baseclass&". -- Roman Yakovenko C++ Python language binding http://www.language-binding.net/
Thank you for the suggestion--unfortunately it does not seem to work for me. In my Python code, I verified that the list is composed of different types of objects. However, in C++ I tried std::cout << typeid(extract<baseclass&>(myList[j])).name(); and it shows that all of the extracted objects are of the same class (baseclass). Any other ideas? I want to be able to do the following in C++: for (i=0 to length of list) list[i].evaluate(); where the "evaluate" method is different depending on what kind of object is pulled from the list. Craig Finch --- Roman Yakovenko <roman.yakovenko@gmail.com> wrote:
I have run into a problem that I don't know how to solve with Boost.python, and I can't find the answer online. I have a list of objects in Python, and the objects are of different classes. All
classes are derived from a common base class, and the classes are defined in C++ and exposed to Python. The list of objects is created in Python, and I would like to get this list into C++, where the
On 6/27/07, Craig Finch <oanjao@yahoo.com> wrote: the the
objects of different types will be handled differently. I can bring the list into C++ as a boost::python::list, but then I have to use extract <type> to get the individual objects from the list. The problem is that extract<baseclass> works, but then all of the extracted objects are the same type. The information contained in the derived types is lost.
Try to extract "baseclass&".
-- Roman Yakovenko C++ Python language binding http://www.language-binding.net/
-------------- Please reply to cfinch@ieee.org ___________________________________________________________________________________ You snooze, you lose. Get messages ASAP with AutoCheck in the all-new Yahoo! Mail Beta. http://advision.webevents.yahoo.com/mailbeta/newmail_html.html
Craig Finch wrote:
Thank you for the suggestion--unfortunately it does not seem to work for me. In my Python code, I verified that the list is composed of different types of objects. However, in C++ I tried
std::cout << typeid(extract<baseclass&>(myList[j])).name();
and it shows that all of the extracted objects are of the same class (baseclass). Any other ideas? I want to be able to do the following in C++:
for (i=0 to length of list) list[i].evaluate();
where the "evaluate" method is different depending on what kind of object is pulled from the list.
Sorry if this feels like an "is it plugged in" type of question, but your baseclass is polymorphic (has virtual methods) right? And when you wrap the derived classes, you use boost::python::bases<> to inform the library of the hierarchy, yes? If you could post a minimal, complete example, it would be easier to diagnose the problem. Alex
If you could post a minimal, complete example, it would be easier to diagnose the problem.
Here is a simple example of what I am trying to do. There is no problem if I know in advance the class of the objects to be imported. However, I don't know the class until runtime, so I don't know how set the appropriate class for extract <???>. ---------------- C++ CODE--------------- class BaseClass { public: virtual void evaluate(void); }; class A : public BaseClass { public: int index; virtual void evaluate(void); }; class B : public BaseClass { public: float value; virtual void evaluate(void); }; class Importer { public: void setList (boost::python::list l); }; void BaseClass::evaluate (void) { std::cout << "Useless base class" << std::endl; } void A::evaluate (void) { std::cout << "Index is " << index << std::endl; } void B::evaluate (void) { std::cout << "Value is " << value << std::endl; } using namespace boost::python; void BaseClass::evaluate (void) { std::cout << "Useless base class" << std::endl; } void A::evaluate (void) { std::cout << "Index is " << index << std::endl; } void B::evaluate (void) { std::cout << "Value is " << value << std::endl; } void Importer::setList (list l) { BaseClass o; int len = extract<int>(l.attr("__len__")()); for (int i=0; i<len; i++) { std::cout << typeid(extract <BaseClass&> (l[i])).name() << std::endl; o = extract <BaseClass&> (l[i]); o.evaluate(); } } BOOST_PYTHON_MODULE(C_test) { class_<BaseClass> ("BaseClass"); class_<A, bases<BaseClass> > ("A") .def("evaluate", &A::evaluate) .def_readwrite("index", &A::index) ; class_<B, bases<BaseClass> > ("B") .def("evaluate", &B::evaluate) .def_readwrite("value", &B::value) ; class_<Importer> ("Importer") .def("setList", &Importer::setList) ; } -------------- PYTHON CODE ------------------ import C_test # Derived Classes class A (C_test.A): def __init__ (self, int): C_test.A.__init__(self) self.index = int class B (C_test.B): def __init__ (self, float): C_test.B.__init__(self) self.value = float mixedList = [] mixedList.append(A(3)) mixedList.append(B(5.234)) imp = C_test.Importer() imp.setList(mixedList) --------------- OUTPUT ---------------- N5boost6python7extractIR9BaseClassEE Useless base class N5boost6python7extractIR9BaseClassEE Useless base class --------------------------------------- -------------- Please reply to cfinch@ieee.org ____________________________________________________________________________________ Never miss an email again! Yahoo! Toolbar alerts you the instant new Mail arrives. http://tools.search.yahoo.com/toolbar/features/mail/
Hi Craig, I've played with your example and I've got it to work by changing your extract<BaseClass> and extract<BaseClass&> into extract<BaseClass*> and extract<const BaseClass*>. In the first case (extract<BaseClass>) you were extracting the object in the list into a *copy*, which was of type BaseClass, so the "Useless base class" function of that copy was then called. By extracting into a pointer (via extract<BaseClass*>) you aren't performing a copy, and you obtain a polymorphic pointer to the original object. As for the typeid part of the example, I don't know why typeid gets it wrong when you use extract<BaseClass&>. I had to change this to extract<const BaseClass*> and then do a typeid of the dereference of the resulting polymorphic pointer. Here is the modified version of 'setList' that worked on my box (linux, gcc 3.4, python 2.3); ============================== void Importer::setList (list l) { BaseClass *o; int len = extract<int>(l.attr("__len__")()); for (int i=0; i<len; i++) { std::cout << typeid( *(extract <const BaseClass*> (l[i])) ).name() << std::endl; o = extract <BaseClass*> (l[i]); o->evaluate(); } } ============================== This gave output; ============================== 1A Index is 3 1B Value is 5.234 ============================== I hope this helps, Christopher
Chris, Your suggestion worked for me. I constructed a simple but complete example of how to handle objects of different types when bringing a Boost.python list into C++ for processing. An explanation and the necessary files are posted on my software development blog at http://www.shocksolution.com/blog/2007/08/08/mixing-objects-of-different-typ... for anyone else who wants to know how to do this. Thanks guys! Craig --- Christopher Woods <chryswoods@gmail.com> wrote:
Hi Craig,
I've played with your example and I've got it to work by changing your extract<BaseClass> and extract<BaseClass&> into extract<BaseClass*> and extract<const BaseClass*>. In the first case (extract<BaseClass>) you were extracting the object in the list into a *copy*, which was of type BaseClass, so the "Useless base class" function of that copy was then called. By extracting into a pointer (via extract<BaseClass*>) you aren't performing a copy, and you obtain a polymorphic pointer to the original object.
As for the typeid part of the example, I don't know why typeid gets it wrong when you use extract<BaseClass&>. I had to change this to extract<const BaseClass*> and then do a typeid of the dereference of the resulting polymorphic pointer.
Here is the modified version of 'setList' that worked on my box (linux, gcc 3.4, python 2.3);
============================== void Importer::setList (list l) { BaseClass *o; int len = extract<int>(l.attr("__len__")());
for (int i=0; i<len; i++) { std::cout << typeid( *(extract <const BaseClass*> (l[i])) ).name() << std::endl;
o = extract <BaseClass*> (l[i]); o->evaluate(); } } ==============================
This gave output;
============================== 1A Index is 3 1B Value is 5.234 ==============================
I hope this helps,
Christopher
-------------- Please reply to cfinch@ieee.org ____________________________________________________________________________________ Be a better Heartthrob. Get better relationship answers from someone who knows. Yahoo! Answers - Check it out. http://answers.yahoo.com/dir/?link=list&sid=396545433
participants (4)
-
Alex Mohr -
Christopher Woods -
Craig Finch -
Roman Yakovenko