Thread: [pygccxml-development] questions about inheritance
Brought to you by:
mbaas,
roman_yakovenko
From: Kevin W. <kev...@gm...> - 2008-02-28 17:33:03
|
I'm using Py++ with a complex class hierarchy with lots of virtual methods. As one example, wxWindow inherits from wxObject. But Py++ is generating class wrappers which don't reflect any inheritance at all. The class declaration for wxWindow looks like bp::class_< wxWindow, boost::noncopyable >( "Window" ) .def( bp::init< >() ) .def( bp::init< wxWindow *, wxWindowID, bp::optional< wxPoint const &, wxSize const &, long int, wxString const & > >(( bp::arg("parent"), bp::arg("winid"), bp::arg("pos")=wxDefaultPosition, bp::arg("size")=wxDefaultSize, bp::arg("style")=(long int)(0), bp::arg("name")=wxString(wxPanelNameStr) )) ) Note the missing bp::bases. Oddly enough, if I load up a module builder in a console and query wxWindow for its bases with the "recursive_bases" property, I see that pygccxml has successively pulled out a chain of classes that looks correct: "wxWindow -> wxCocoaNSView -> wxWindowBase -> wxEvtHandler -> wxTrackable -> wxObject" By which criteria does Py++ decide to expose inhertiance? Do I need to "include" each of the classes listed in wxWindow's recursive_bases property? What if I want just the wxObject methods to be exposed? (For reference, my code generator is at http://code.google.com/p/wxpy/source/browse/trunk/generate_code.py) Any help is much appreciated! |
From: Roman Y. <rom...@gm...> - 2008-02-28 19:54:56
|
On Thu, Feb 28, 2008 at 7:33 PM, Kevin Watters <kev...@gm...> wrote: > I'm using Py++ with a complex class hierarchy with lots of virtual methods. It should not be a problem > As one example, wxWindow inherits from wxObject. But Py++ is > generating class wrappers which don't reflect any inheritance at all. > The class declaration for wxWindow looks like > > bp::class_< wxWindow, boost::noncopyable >( "Window" ) > .def( bp::init< >() ) > .def( bp::init< wxWindow *, wxWindowID, bp::optional< wxPoint > const &, wxSize const &, long int, wxString const & > >(( > bp::arg("parent"), bp::arg("winid"), bp::arg("pos")=wxDefaultPosition, > bp::arg("size")=wxDefaultSize, bp::arg("style")=(long int)(0), > bp::arg("name")=wxString(wxPanelNameStr) )) ) > > > Note the missing bp::bases. > > Oddly enough, if I load up a module builder in a console and query > wxWindow for its bases with the "recursive_bases" property, I see that > pygccxml has successively pulled out a chain of classes that looks > correct: "wxWindow -> wxCocoaNSView -> wxWindowBase -> wxEvtHandler -> > wxTrackable -> wxObject" > > By which criteria does Py++ decide to expose inhertiance? Do I need to > "include" each of the classes listed in wxWindow's recursive_bases > property? What if I want just the wxObject methods to be exposed? I read your script. I think I understand what the problem is. Take a look on this document. http://language-binding.net/pyplusplus/documentation/tutorials/module_builder/module_builder.html#declarations-customization I guess this is the problem. You can check it by testing "ignore" value mb.class_( 'wxObject' ).ignore Basically I suggest you to exclude everything and than to include all declarations you want to expose. mb = module_builder_t(...) mb.global_ns.exclude() mb.classes( lambda decl: decl.startswith( 'wx' ) ).include() > (For reference, my code generator is at > http://code.google.com/p/wxpy/source/browse/trunk/generate_code.py) Why do you use so much try...except blocks? For example: try: mb.mem_funs(name = 'CloneGDIRefData').exclude() except: pass If the function is there Py++ will find it an exclude, if it not there, than exception will be thrown and you will be noted that the were some changes in source files? I ask, because I want to understand whether the problem in Py++ interface or this is just your use case. > Any help is much appreciated! HTH -- Roman Yakovenko C++ Python language binding http://www.language-binding.net/ |
From: Kevin W. <kev...@gm...> - 2008-02-29 00:17:59
|
> I guess this is the problem. You can check it by testing "ignore" value > > mb.class_( 'wxObject' ).ignore This helps--because like you suggest, I started out by excluding all declarations and then slowly moving things in so that I could keep the number of errors under control. With this ignore property, I noticed that some of the bases of wxFrame, for instance, were being excluded. I was under the impression that mb.class_('wxFrame').include() would include all the bases of wxFrame as well--but apparently that is not the case. > Why do you use so much try...except blocks? > > For example: > try: mb.mem_funs(name = 'CloneGDIRefData').exclude() > except: pass I've developing this simultaneously on OS X and Windows, and some of the methods differ--or are missing entirely, depending on the platform. Thanks again for your help! - Kevin |
From: Kevin W. <kev...@gm...> - 2008-03-01 18:03:11
|
Say I have class A { public: virtual void DoSomething() = 0; }; and class B { public: void Something() { /* ... */ } virtual void DoSomething() { Something(); } }; I want to hide the DoSomething methods from Python, but keep Something exposed. So I do mb.calldefs(lambda d: d.name.startswith('Do')).exclude() to hide the DoSomethings(). The problem is, class A has other methods that I need, so I use A.include() to include it. I get compile errors, saying "cannot instantiate abstract class." I think what is happening, is that by excluding the pure virtual functions in A, I'm fooling Py++ into generating invalid wrappers for A (since all the pure virtual functions are "hidden"). Does "exclude" work this way? If not, Py++ must be getting confused about some other aspect of the classes and I'll have to investigate further. (Ideally what I really want is to finalize ALL classes unless I say so explicitly that a method should be overridden. This seems ideal for performance. I found the finalize function in the contrib folder, and it seems to work, but I'm still not sure how to handle the more complex case outlined above.) I also have another, entirely unrelated question :-D In the class hierachy I'm attempting to get something working with, there is an entire category of classes (all with one common base class) that are "owned" entirely by C++, since they have operating system window resources. When you create a button, for instance, you attach it to a window, and the window is responsible for destroying it. You call something like window->Destroy(), and then the Python reference to the button has to become "invalid." The other wrapper for this GUI library that exists (created with SWIG) has a clever solution where the __class__ of the object is assigned to a special "DeadObject" class, and any further attribute accesses on the object raise Exceptions (instead of crash the program ;) ). I'm having trouble figuring out to achieve this kind of behavior with boost. Basically, the object lifetime of a wxButton, for instance, is not at all determined by any python objects which may reference it. It's constructor takes a wxWindow* parent which immediately gains ownership of the wxButton, and Destroys its children when it is Destroyed. If the Python reference to the button goes away, nothing at all should happen. How does that idea fit into Boost's object lifetime management, if at all? On Thu, Feb 28, 2008 at 2:55 PM, Roman Yakovenko <rom...@gm...> wrote: > On Thu, Feb 28, 2008 at 7:33 PM, Kevin Watters <kev...@gm...> wrote: > > I'm using Py++ with a complex class hierarchy with lots of virtual methods. > > It should not be a problem > > > > As one example, wxWindow inherits from wxObject. But Py++ is > > generating class wrappers which don't reflect any inheritance at all. > > The class declaration for wxWindow looks like > > > > bp::class_< wxWindow, boost::noncopyable >( "Window" ) > > .def( bp::init< >() ) > > .def( bp::init< wxWindow *, wxWindowID, bp::optional< wxPoint > > const &, wxSize const &, long int, wxString const & > >(( > > bp::arg("parent"), bp::arg("winid"), bp::arg("pos")=wxDefaultPosition, > > bp::arg("size")=wxDefaultSize, bp::arg("style")=(long int)(0), > > bp::arg("name")=wxString(wxPanelNameStr) )) ) > > > > > > Note the missing bp::bases. > > > > Oddly enough, if I load up a module builder in a console and query > > wxWindow for its bases with the "recursive_bases" property, I see that > > pygccxml has successively pulled out a chain of classes that looks > > correct: "wxWindow -> wxCocoaNSView -> wxWindowBase -> wxEvtHandler -> > > wxTrackable -> wxObject" > > > > By which criteria does Py++ decide to expose inhertiance? Do I need to > > "include" each of the classes listed in wxWindow's recursive_bases > > property? What if I want just the wxObject methods to be exposed? > > I read your script. I think I understand what the problem is. > Take a look on this document. > http://language-binding.net/pyplusplus/documentation/tutorials/module_builder/module_builder.html#declarations-customization > > I guess this is the problem. You can check it by testing "ignore" value > > mb.class_( 'wxObject' ).ignore > > Basically I suggest you to exclude everything and than to include all > declarations you want to expose. > mb = module_builder_t(...) > mb.global_ns.exclude() > > mb.classes( lambda decl: decl.startswith( 'wx' ) ).include() > > > > > (For reference, my code generator is at > > http://code.google.com/p/wxpy/source/browse/trunk/generate_code.py) > > Why do you use so much try...except blocks? > > For example: > try: mb.mem_funs(name = 'CloneGDIRefData').exclude() > except: pass > > If the function is there Py++ will find it an exclude, if it not > there, than exception will be thrown and you will be noted that the > were some changes in source files? > > I ask, because I want to understand whether the problem in Py++ > interface or this is just your use case. > > > > Any help is much appreciated! > > HTH > > -- > Roman Yakovenko > C++ Python language binding > http://www.language-binding.net/ > |
From: Roman Y. <rom...@gm...> - 2008-03-01 20:03:56
|
On Sat, Mar 1, 2008 at 8:03 PM, Kevin Watters <kev...@gm...> wrote: > Say I have > > class A { > public: > virtual void DoSomething() = 0; > }; > > and > > class B { > public: > void Something() { /* ... */ } > virtual void DoSomething() { Something(); } > }; > > I want to hide the DoSomething methods from Python, but keep Something exposed. > > So I do > > mb.calldefs(lambda d: d.name.startswith('Do')).exclude() > > to hide the DoSomethings(). The problem is, class A has other methods > that I need, so I use A.include() to include it. So you can change the order of exclude's > I get compile errors, saying "cannot instantiate abstract class." I > think what is happening, is that by excluding the pure virtual > functions in A, I'm fooling Py++ into generating invalid wrappers for > A (since all the pure virtual functions are "hidden"). Does "exclude" > work this way? I always thought that Py++ generates code that compiles even if "pure virtual" functions are excluded. I just added new test and saw that this is not the case. I will fix the problem in next few days. Thanks for bug reporting. > If not, Py++ must be getting confused about some other > aspect of the classes and I'll have to investigate further. > (Ideally what I really want is to finalize ALL classes unless I say so > explicitly that a method should be overridden. This seems ideal for > performance. I found the finalize function in the contrib folder, and > it seems to work, but I'm still not sure how to handle the more > complex case outlined above.) As I said: I will fix the bug. > I also have another, entirely unrelated question :-D And you will get entirely unrelated answer :-) > In the class hierachy I'm attempting to get something working with, > there is an entire category of classes (all with one common base > class) that are "owned" entirely by C++, since they have operating > system window resources. When you create a button, for instance, you > attach it to a window, and the window is responsible for destroying > it. You call something like window->Destroy(), and then the Python > reference to the button has to become "invalid." > > The other wrapper for this GUI library that exists (created with SWIG) > has a clever solution where the __class__ of the object is assigned to > a special "DeadObject" class, and any further attribute accesses on > the object raise Exceptions (instead of crash the program ;) ). I'm > having trouble figuring out to achieve this kind of behavior with > boost. Basically, the object lifetime of a wxButton, for instance, is > not at all determined by any python objects which may reference it. > It's constructor takes a wxWindow* parent which immediately gains > ownership of the wxButton, and Destroys its children when it is > Destroyed. If the Python reference to the button goes away, nothing at > all should happen. How does that idea fit into Boost's object > lifetime management, if at all? I need to think a little about this. It could be nice if you can create small test case, so I could play with it. -- Roman Yakovenko C++ Python language binding http://www.language-binding.net/ |
From: Kevin W. <kev...@gm...> - 2008-03-01 21:25:46
|
> I always thought that Py++ generates code that compiles even if "pure > virtual" functions are excluded. I just added new test and saw that > this is not the case. > > I will fix the problem in next few days. > > Thanks for bug reporting. Thank you very much for this! I'll probably be hunting sooner than that for the cause of this--I'll let you know if I find anything ;) > > In the class hierachy I'm attempting to get something working with, > > there is an entire category of classes (all with one common base > > class) that are "owned" entirely by C++ > I need to think a little about this. It could be nice if you can > create small test case, so I could play with it. I really appreciate your help so far. I made a small demo of what I'm talking about specifically: A small set of classes that mirror the ones I'm trying to wrap: http://code.google.com/p/wxpy/source/browse/trunk/docs/window_lifetimes/window_lifetime.h And some example Python and C++ for interacting with those classes: http://code.google.com/p/wxpy/source/browse/trunk/docs/window_lifetimes/window_lifetime.cpp If nothing else I hope it helps you understand what I mean. Thanks again! - Kevin |
From: Kevin W. <kev...@gm...> - 2008-03-03 05:01:21
|
> The fix in SVN. After trying your change from SVN I still get the same error: /Users/kevin/src/boost_1_34_1/boost/python/object/value_holder.hpp:66: error: cannot declare field 'boost::python::objects::value_holder<wxWindowBase_wrapper>::m_held' to be of abstract type 'wxWindowBase_wrapper' ../src/generated/WindowBase.pypp.cpp:9: note: because the following virtual functions are pure within 'wxWindowBase_wrapper': /Users/kevin/src/wxWidgets/include/wx/window.h:1278: note: virtual NSView* wxWindowBase::GetHandle() const /Users/kevin/src/wxWidgets/include/wx/window.h:1511: note: virtual void wxWindowBase::DoClientToScreen(int*, int*) const /Users/kevin/src/wxWidgets/include/wx/window.h:1512: note: virtual void wxWindowBase::DoScreenToClient(int*, int*) const /Users/kevin/src/wxWidgets/include/wx/window.h:1517: note: virtual void wxWindowBase::DoCaptureMouse() /Users/kevin/src/wxWidgets/include/wx/window.h:1518: note: virtual void wxWindowBase::DoReleaseMouse() /Users/kevin/src/wxWidgets/include/wx/window.h:1521: note: virtual void wxWindowBase::DoGetPosition(int*, int*) const /Users/kevin/src/wxWidgets/include/wx/window.h:1523: note: virtual void wxWindowBase::DoGetSize(int*, int*) const /Users/kevin/src/wxWidgets/include/wx/window.h:1524: note: virtual void wxWindowBase::DoGetClientSize(int*, int*) const /Users/kevin/src/wxWidgets/include/wx/window.h:1536: note: virtual void wxWindowBase::DoSetSize(int, int, int, int, int) /Users/kevin/src/wxWidgets/include/wx/window.h:1539: note: virtual void wxWindowBase::DoSetClientSize(int, int) /Users/kevin/src/wxWidgets/include/wx/window.h:1553: note: virtual void wxWindowBase::DoMoveWindow(int, int, int, int) /Users/kevin/src/wxWidgets/include/wx/window.h:1565: note: virtual bool wxWindowBase::DoPopupMenu(wxMenu*, int, int) make: *** [obj-gnu/wxpy_WindowBase_pypp.o] Error 1 I'm trying to understand what the change you made to class_wrapper.redefined_funcs is doing. If I get anywhere I will let you know. - Kevin |
From: Roman Y. <rom...@gm...> - 2008-03-03 20:23:42
|
On Mon, Mar 3, 2008 at 7:01 AM, Kevin Watters <kev...@gm...> wrote: > > The fix in SVN. > > After trying your change from SVN I still get the same error: > > /Users/kevin/src/boost_1_34_1/boost/python/object/value_holder.hpp:66: > error: cannot declare field > 'boost::python::objects::value_holder<wxWindowBase_wrapper>::m_held' > to be of abstract type 'wxWindowBase_wrapper' > ../src/generated/WindowBase.pypp.cpp:9: note: because the following > virtual functions are pure within 'wxWindowBase_wrapper': > /Users/kevin/src/wxWidgets/include/wx/window.h:1278: note: virtual > NSView* wxWindowBase::GetHandle() const > /Users/kevin/src/wxWidgets/include/wx/window.h:1511: note: virtual > void wxWindowBase::DoClientToScreen(int*, int*) const > /Users/kevin/src/wxWidgets/include/wx/window.h:1512: note: virtual > void wxWindowBase::DoScreenToClient(int*, int*) const > /Users/kevin/src/wxWidgets/include/wx/window.h:1517: note: virtual > void wxWindowBase::DoCaptureMouse() > /Users/kevin/src/wxWidgets/include/wx/window.h:1518: note: virtual > void wxWindowBase::DoReleaseMouse() > /Users/kevin/src/wxWidgets/include/wx/window.h:1521: note: virtual > void wxWindowBase::DoGetPosition(int*, int*) const > /Users/kevin/src/wxWidgets/include/wx/window.h:1523: note: virtual > void wxWindowBase::DoGetSize(int*, int*) const > /Users/kevin/src/wxWidgets/include/wx/window.h:1524: note: virtual > void wxWindowBase::DoGetClientSize(int*, int*) const > /Users/kevin/src/wxWidgets/include/wx/window.h:1536: note: virtual > void wxWindowBase::DoSetSize(int, int, int, int, int) > /Users/kevin/src/wxWidgets/include/wx/window.h:1539: note: virtual > void wxWindowBase::DoSetClientSize(int, int) > /Users/kevin/src/wxWidgets/include/wx/window.h:1553: note: virtual > void wxWindowBase::DoMoveWindow(int, int, int, int) > /Users/kevin/src/wxWidgets/include/wx/window.h:1565: note: virtual > bool wxWindowBase::DoPopupMenu(wxMenu*, int, int) > make: *** [obj-gnu/wxpy_WindowBase_pypp.o] Error 1 > > I'm trying to understand what the change you made to > class_wrapper.redefined_funcs is doing. If I get anywhere I will let > you know. Can you create small test case, that reproduce the problem? I tried to create without success: http://pygccxml.svn.sourceforge.net/viewvc/pygccxml?view=rev&revision=1268 http://pygccxml.svn.sourceforge.net/viewvc/pygccxml?view=rev&revision=1269 http://pygccxml.svn.sourceforge.net/viewvc/pygccxml?view=rev&revision=1270 Thanks. -- Roman Yakovenko C++ Python language binding http://www.language-binding.net/ |