hi there, now that I have taken the first hurdles, I come back with more questions :) I want to create a python frontend for a set of libraries / modules I'm working with. I'v got an interface ('Foo') that is implemented at a couple of places ('FooImpl1', 'FooImpl2', etc.), and I have a factory that instantiates them depending on a string argument. It looks like the natural thing to do would be to make the python front end expose the interface 'Foo', but instead of exposing Foo's constructor, the Foo factory is used:
foo1 = Foo("FooImpl1") foo2 = Foo("FooImpl2")
How is this to be done ? It looks like I should play with 'HeldType', but I could't get it working yet. Also, If I use a 'class_' where the HeldType is a pointer type, how should I manage ownership ? Thanks a lot, Stefan
Stefan Seefeld <stefan.seefeld@orthosoft.ca> writes:
hi there,
now that I have taken the first hurdles, I come back with more questions :)
I want to create a python frontend for a set of libraries / modules I'm working with.
You've come to the right place!
I'v got an interface ('Foo') that is implemented at a couple of places ('FooImpl1', 'FooImpl2', etc.), and I have a factory that instantiates them depending on a string argument.
It looks like the natural thing to do would be to make the python front end expose the interface 'Foo', but instead of exposing Foo's constructor, the Foo factory is used:
foo1 = Foo("FooImpl1") foo2 = Foo("FooImpl2")
How is this to be done ? It looks like I should play with 'HeldType', but I could't get it working yet.
How about using boost::shared_ptr<Foo> as your HeldType and returning boost::shared_ptr<Foo> from your factory function?
Also, If I use a 'class_' where the HeldType is a pointer type, how should I manage ownership ?
Best not to use a raw pointer, since (I guess it's obvious) managing ownership is tough. If you use boost::shared_ptr or std::auto_ptr ownership will be managed for you. HTH, Dave -- David Abrahams * Boost Consulting dave@boost-consulting.com * http://www.boost-consulting.com
David Abrahams wrote:
How about using boost::shared_ptr<Foo> as your HeldType and returning boost::shared_ptr<Foo> from your factory function?
ok. Now I have python::class_<Foo, boost::shared_ptr<Foo>, boost::noncopyable> hello("Foo"); but the compiler complains. You use a 'assert_default_constructible' class to make sure 'Foo' can be default-constructed, though obviously in my case it shouldn't (only the factory should construct 'Foo's). What am I missing ? Stefan
Stefan Seefeld <seefeld@sympatico.ca> writes:
David Abrahams wrote:
How about using boost::shared_ptr<Foo> as your HeldType and returning boost::shared_ptr<Foo> from your factory function?
ok. Now I have
python::class_<Foo, boost::shared_ptr<Foo>, boost::noncopyable> hello("Foo");
but the compiler complains. You use a 'assert_default_constructible' class to make sure 'Foo' can be default-constructed, though obviously in my case it shouldn't (only the factory should construct 'Foo's). What am I missing ?
python::class_<Foo, boost::shared_ptr<Foo>, boost::noncopyable> hello("Foo", no_init); ^^^^^^^ -or- python::class_<Foo, boost::shared_ptr<Foo>, boost::noncopyable> hello("Foo", init<Arg1, Arg2>()); etc... -- David Abrahams * Boost Consulting dave@boost-consulting.com * http://www.boost-consulting.com
Stefan Seefeld <seefeld@sympatico.ca> writes:
Hi David,
David Abrahams wrote:
python::class_<Foo, boost::shared_ptr<Foo>, boost::noncopyable> hello("Foo", no_init);
works wonderfully !
I want to add nested types to my class (enums, consts, other classes, etc.). How is this to be done ?
See http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/*checkout*/boost/boost/libs/p... Hmm, it's clear to me that many pages are missing from the reference documentation index. -- David Abrahams * Boost Consulting dave@boost-consulting.com * http://www.boost-consulting.com
Hi David, thanks a lot for your prompt replies, they are extremely helpful. I now want to wrap a method that returns a 'Bar' pointer (in fact, what is returned is a 'BarImpl', derived from 'Bar'). class Bar {}; class BarImpl : public Bar {}; class Foo { public: BarImpl *GetBar(); }; I'v declared a python class for 'Bar': python::class_<Bar, boost::shared_ptr<Bar>, boost::noncopyable> ti("Bar", python::no_init); but I figure I need to tell boost how to treat a 'BarImpl *' first, i.e. how to coerce a 'BarImpl *' into a boost::shared_ptr<Bar>. I'v come across the 'ResultConverter' stuff, but couldn't make much use of it so far. Any hints ? Thanks again, Stefan
Stefan Seefeld <seefeld@sympatico.ca> writes:
Hi David,
thanks a lot for your prompt replies, they are extremely helpful.
I now want to wrap a method that returns a 'Bar' pointer (in fact, what is returned is a 'BarImpl', derived from 'Bar').
class Bar {}; class BarImpl : public Bar {}; class Foo { public: BarImpl *GetBar(); };
OK, first I need to ask some questions: 1. Are you trying to wrap an interface without changing it? In other words, is it OK to change the definition of Foo, Bar, and BarImpl, or is this wrapping job to be non-intrusive? 2. What is the semantics of GetBar()? Does it just return a pointer to some BarImpl, to be used as a kind of reference, or is it the caller expected to delete (or otherwise manage) the pointer it gets back? 3. If the BarImpl* is just a kind of reference, which object actually manages the lifetime of the BarImpl object?
I'v declared a python class for 'Bar':
python::class_<Bar, boost::shared_ptr<Bar>, boost::noncopyable> ti("Bar", python::no_init);
but I figure I need to tell boost how to treat a 'BarImpl *' first, i.e. how to coerce a 'BarImpl *' into a boost::shared_ptr<Bar>.
No, not neccessarily. Maybe it would be better to stop using boost::shared_ptr<Bar> in your class_<> definition for the time being, and switch to using return_value_policy<manage_new_object>() for your factory function instead... just to avoid confusion.
I'v come across the 'ResultConverter' stuff, but couldn't make much use of it so far. Any hints ?
Try answering my questions above first, and we'll see which hints are appropriate. -- David Abrahams * Boost Consulting dave@boost-consulting.com * http://www.boost-consulting.com
David Abrahams wrote:
1. Are you trying to wrap an interface without changing it? In other words, is it OK to change the definition of Foo, Bar, and BarImpl, or is this wrapping job to be non-intrusive?
ideally it's non-intrusive. I'm all for cleaning it up (and it needs some cleanup !), but that's not my (current) mandate...
2. What is the semantics of GetBar()? Does it just return a pointer to some BarImpl, to be used as a kind of reference, or is it the caller expected to delete (or otherwise manage) the pointer it gets back?
in this particular case Foo remains the owner.
3. If the BarImpl* is just a kind of reference, which object actually manages the lifetime of the BarImpl object?
Foo. I guess in all other cases I would use the factory approach we discussed yesterday. Regards, Stefan
Stefan Seefeld <seefeld@sympatico.ca> writes:
David Abrahams wrote:
1. Are you trying to wrap an interface without changing it? In other words, is it OK to change the definition of Foo, Bar, and BarImpl, or is this wrapping job to be non-intrusive?
ideally it's non-intrusive. I'm all for cleaning it up (and it needs some cleanup !), but that's not my (current) mandate...
2. What is the semantics of GetBar()? Does it just return a pointer to some BarImpl, to be used as a kind of reference, or is it the caller expected to delete (or otherwise manage) the pointer it gets back?
in this particular case Foo remains the owner.
3. If the BarImpl* is just a kind of reference, which object actually manages the lifetime of the BarImpl object?
Foo.
I guess in all other cases I would use the factory approach we discussed yesterday.
Regards, Stefan
Okay, revisiting your code in that light: class Bar {}; class BarImpl : public Bar {}; class Foo { public: BarImpl *GetBar(); }; you need this part no matter what: class_<Bar>("Bar") ... ; option 1: expose the BarImpl class too: class_<BarImpl, bases<Bar> >("BarImpl", no_init) ; class_<Foo>("Foo") .def("GetBar", &Foo::GetBar , return_value_policy<return_internal_reference>()) ... ; The only downside of that is that Python users might detect that there's a BarImpl class as well as a Bar class. But so what? option 2: use a thin wrapper to hide the existence of BarImpl: Bar* foo_GetBar(Foo& f) { return f.GetBar(); } class_<Foo>("Foo") .def("GetBar", foo_GetBar , return_value_policy<return_internal_reference>()) ... ; HTH, Dave P.S. I suggest you read up on CallPolicies in the documentation I sent you pointers to. -- David Abrahams * Boost Consulting dave@boost-consulting.com * http://www.boost-consulting.com
David Abrahams <dave@boost-consulting.com> writes:
class_<Foo>("Foo") .def("GetBar", &Foo::GetBar , return_value_policy<return_internal_reference>()) ... ;
Whoops! All of those places I've written <return_internal_reference> it should say <return_internal_reference<> > instead. Sorry, Dave -- David Abrahams * Boost Consulting dave@boost-consulting.com * http://www.boost-consulting.com
David Abrahams wrote:
you need this part no matter what:
class_<Bar>("Bar") ... ;
with just one 'Bar' template parameter ? Wouldn't that mean that each python instance would hold its own 'Bar' *object* ? I'm dealing with references (pointers), after all, whether I own them or not...
option 2: use a thin wrapper to hide the existence of BarImpl:
Bar* foo_GetBar(Foo& f) { return f.GetBar(); }
class_<Foo>("Foo") .def("GetBar", foo_GetBar , return_value_policy<return_internal_reference>()) ... ;
ok, tried that (plus the '<>' you mention in another mail), but I get linker errors as some types are not to be found, or incomplete. But what speaks against using shared_ptr with my own (dummy) deleter ? I try this: template<class T> struct dummy_deleter { typedef void result_type; typedef T * argument_type; void operator()(T * x){} }; boost::shared_ptr<Bar> Foo_GetBar(Foo &foo, int i) { return boost::shared_ptr<Bar>(foo.getBar(), dummy_deleter<Foo>()); } and then simply declare foo.def("getBar", Foo_GetBar); and it works. Is this a 'correct' way of doing it ? Stefan
Stefan Seefeld <seefeld@sympatico.ca> writes:
David Abrahams wrote:
you need this part no matter what: class_<Bar>("Bar") ... ;
with just one 'Bar' template parameter ? Wouldn't that mean that each python instance would hold its own 'Bar' *object* ?
Not neccessarily. It just affects how the Bar object is held when you construct one from Python:
b = Bar() # Held by-value or,e.g., by shared_ptr<Bar>?
I'm dealing with references (pointers), after all, whether I own them or not...
That's OK. When you use return_value_policy<manage_new_object> the pointer gets embedded in and managed by a new Python object. When you use return_value_policy<return_internal_reference<> > the pointer gets embedded in a new Python object and the other Python object wrappers have their lifetimes tied together so that the containing object won't be destroyed until the new object is.
option 2: use a thin wrapper to hide the existence of BarImpl: Bar* foo_GetBar(Foo& f) { return f.GetBar(); } class_<Foo>("Foo") .def("GetBar", foo_GetBar , return_value_policy<return_internal_reference>()) ... ;
ok, tried that (plus the '<>' you mention in another mail), but I get linker errors as some types are not to be found, or incomplete.
I can't help with that unless you post the linker errors.
But what speaks against using shared_ptr with my own (dummy) deleter ?
I try this:
template<class T> struct dummy_deleter { typedef void result_type; typedef T * argument_type;
void operator()(T * x){} };
boost::shared_ptr<Bar> Foo_GetBar(Foo &foo, int i) { return boost::shared_ptr<Bar>(foo.getBar(), dummy_deleter<Foo>()); }
Umm, memory leaks and dangling shared_ptrs do.
and then simply declare
foo.def("getBar", Foo_GetBar);
and it works. Is this a 'correct' way of doing it ?
Not very, unless it's OK for Python code using your wrapper to crash the system. The C++ Bar object can still be deleted out from under the shared_ptr that refers to it. -- David Abrahams * Boost Consulting dave@boost-consulting.com * http://www.boost-consulting.com
David Abrahams <dave@boost-consulting.com> writes:
Stefan Seefeld <stefan.seefeld@orthosoft.ca> writes:
I'v got an interface ('Foo') that is implemented at a couple of places ('FooImpl1', 'FooImpl2', etc.), and I have a factory that instantiates them depending on a string argument.
It looks like the natural thing to do would be to make the python front end expose the interface 'Foo', but instead of exposing Foo's constructor, the Foo factory is used:
foo1 = Foo("FooImpl1") foo2 = Foo("FooImpl2")
How is this to be done ? It looks like I should play with 'HeldType', but I could't get it working yet.
How about using boost::shared_ptr<Foo> as your HeldType and returning boost::shared_ptr<Foo> from your factory function?
Also, If I use a 'class_' where the HeldType is a pointer type, how should I manage ownership ?
Best not to use a raw pointer, since (I guess it's obvious) managing ownership is tough. If you use boost::shared_ptr or std::auto_ptr ownership will be managed for you.
Actually I forgot that there's support for using raw pointers here, if you want it: class_<Base, noncopyable>("Base", no_init) ... // whatever ; Now, def("Foo", Foo, return_value_policy<manage_new_object>()); See http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/*checkout*/boost/boost/libs/p... for an example. -- David Abrahams * Boost Consulting dave@boost-consulting.com * http://www.boost-consulting.com
participants (3)
-
David Abrahams -
Stefan Seefeld -
Stefan Seefeld