Tracing down some things to add in validators and I''ve run across something that kinda bothers me... In order to implement validators you have to override the clone method. The directors seems to be set up to specifically handle this situation. However, whenever C++ calls back to the object''s methods the swig_get_up function is returning false. It seems like swig_up should be true most of the time. In fact, I''m thinking that the whole implementation is a little messed up. Is it just that I''m missing something? Roy
Roy Sutton wrote:> In order to implement validators you have to override the clone method. > The directors seems to be set up to specifically handle this situation. > However, whenever C++ calls back to the object''s methods the swig_get_up > function is returning false. It seems like swig_up should be true most > of the time. In fact, I''m thinking that the whole implementation is a > little messed up. Is it just that I''m missing something?Whenever swig is calling back into ruby, swig_up should be true. The sipmlest case is when ruby code overrides a virtual C++ method, which I think is what you are describing. I know the basic mechanism isn''t completely and totally broken because it works for App#on_init. It would help if you could post a little ruby sample fragment, and point to the C++ code that you suspect of being incorrect. Kevin
Attached are two files: test21.rb (a slightly modified version of the file from the previous version of wxRuby) Validator.i Plop validator.i into swig/classes and rake. Try running test21.rb. Open the Edit->''Goto Line'' dialog box. You -should- see ''Validator.Clone was called'' followed by ''Validator.Copy was called'' in the console. But you won''t if it behaves like it does on mine. You can go into Validator.cpp and add a printf into the Clone wrapper and you''ll see swig_up is false. Roy Kevin Smith wrote:> Roy Sutton wrote: > >> In order to implement validators you have to override the clone >> method. The directors seems to be set up to specifically handle this >> situation. However, whenever C++ calls back to the object''s methods >> the swig_get_up function is returning false. It seems like swig_up >> should be true most of the time. In fact, I''m thinking that the >> whole implementation is a little messed up. Is it just that I''m >> missing something? > > > Whenever swig is calling back into ruby, swig_up should be true. The > sipmlest case is when ruby code overrides a virtual C++ method, which > I think is what you are describing. I know the basic mechanism isn''t > completely and totally broken because it works for App#on_init. > > It would help if you could post a little ruby sample fragment, and > point to the C++ code that you suspect of being incorrect. > > Kevin > _______________________________________________ > wxruby-users mailing list > wxruby-users@rubyforge.org > http://rubyforge.org/mailman/listinfo/wxruby-users > > >-------------- next part -------------- # Copyright 2004-2005 by Kevin Smith # released under the MIT-style wxruby2 license %include "../common.i" %module(directors="1") wxValidator %include "include/wxValidator.h" -------------- next part -------------- require ''wx'' include Wx MENU_FILE_OPEN = 100 MENU_FILE_SAVE = 101 MENU_FILE_QUIT = 102 MENU_EDIT_GOTO = 103 MENU_OPTION_BACKGROUND = 104 MENU_OPTION_FONT = 105 MENU_OPTION_DIRECTORY = 106 MENU_INFO_ABOUT = 107 class RangeValidator < Validator attr_reader :min attr_reader :max attr_reader :current def initialize(initialValue, min, max) super() @min = min @max = max @current = initialValue #puts("Validator will allow #{min} to #{max}, default #{current}") end def copy puts("Validator.copy was called") return RangeValidator.new(current, min, max) end def clone puts("Validator.clone was called") return copy end def validate(parent) win = get_window() if !win puts("get_window returned nil") return false end control = win result = false currentValue = control.get_value.to_i puts("Value being checked: #{currentValue}") if currentValue < 1 message = "#{currentValue} is not a valid number" else if currentValue < min || currentValue > max message = "#{currentValue} is not between #{min} and #{max}" else result = true end end if !result message_box(message) control.set_focus end return result end def transfer_to_window puts("transfer_to_window called") puts(current.to_s) win = get_window() control = win control.set_value(current.to_s) puts("Value set to #{current}") return true end def transfer_from_window puts("transfer_from_window called") win = get_window() control = win @current = control.get_value.to_i puts("Got value #{current}") return true end end class GotoLineDialog < Dialog attr_accessor :value def initialize(parent) super(parent, -1, "Goto Line...", DEFAULT_POSITION, Size.new(250, 120), DEFAULT_DIALOG_STYLE) set_auto_layout(TRUE) layout = LayoutConstraints.new # Label is centered on the dialog and on top of the dialog. @m_pLabel = StaticText.new(self, -1, "") layout.top.same_as(self, LAYOUT_TOP, 10) layout.centreX.same_as(self, LAYOUT_CENTRE_X) layout.width.as_is layout.height.as_is @m_pLabel.set_constraints(layout) # LineNumberCtrl is centered on the dialog and below Label. @m_pLineNumberCtrl = TextCtrl.new(self, -1) layout = LayoutConstraints.new layout.top.below(@m_pLabel, 10) layout.centreX.same_as(self, LAYOUT_CENTRE_X) layout.width.absolute(70) layout.height.as_is @m_pLineNumberCtrl.set_constraints(layout) # cancelButton is put at the right and bottom of the dialog. cancelButton = Button.new(self, ID_CANCEL, "Cancel") layout = LayoutConstraints.new layout.bottom.same_as(self, LAYOUT_BOTTOM, 10) layout.right.same_as(self, LAYOUT_RIGHT, 10) layout.width.as_is layout.height.as_is cancelButton.set_constraints(layout) # okButton is put at the left of the cancelButton. okButton = Button.new(self, ID_OK, "Ok") layout = LayoutConstraints.new layout.bottom.same_as(cancelButton, LAYOUT_BOTTOM) layout.right.left_of(cancelButton, 10) layout.width.same_as(cancelButton, LAYOUT_WIDTH) layout.height.as_is okButton.set_constraints(layout) okButton.set_default evt_init_dialog {onInitDialog} end def onInitDialog label = "Enter a line between 1 and #{@m_maxLineNumber}" @m_pLabel.set_label(label) layout() rangeValidator = RangeValidator.new(1, 1, @m_maxLineNumber) @m_pLineNumberCtrl.set_validator(rangeValidator) #puts rangeValidator #puts @m_pLineNumberCtrl.get_validator() result = transfer_data_to_window() #puts("transfer_data_to_window returned #{result}") @m_pLineNumberCtrl.set_focus end def SetMaxLineNumber(maxLineNumber) @m_maxLineNumber = maxLineNumber end def GetLineNumber return @m_pLineNumberCtrl.get_validator.current end end class AboutDialog < Dialog def initialize(parent) super(parent, -1, "About Simple Text Editor", DEFAULT_POSITION, Size.new(200, 200), DEFAULT_DIALOG_STYLE) set_auto_layout(TRUE) layout = LayoutConstraints.new layout.top.same_as(self, LAYOUT_TOP, 10) layout.centreX.same_as(self, LAYOUT_CENTRE_X) layout.width.as_is layout.height.as_is @m_pInfoText = StaticText.new(self, -1, "", Point.new(-1, -1), DEFAULT_SIZE, ALIGN_CENTER) @m_pInfoText.set_constraints(layout) layout = LayoutConstraints.new layout.top.below(@m_pInfoText, 10) layout.centreX.same_as(self, LAYOUT_CENTRE_X) layout.width.percent_of(self, LAYOUT_WIDTH, 80) layout.height.as_is @m_pOkButton = Button.new(self, ID_OK, "Ok", Point.new(-1, -1)) @m_pOkButton.set_constraints(layout) layout() end def set_text(text) @m_pInfoText.set_label(text) # Call layout, because the static text could be resized. layout() end end class TextFrame < Frame def initialize(title, xpos, ypos, width, height) super(nil,-1, title, Point.new(xpos, ypos), Size.new(width, height)) @m_pTextCtrl = TextCtrl.new(self, -1, "This is\na test of\ndialog input.\nFeel free to add more text...", DEFAULT_POSITION, DEFAULT_SIZE, TE_MULTILINE) @m_pMenuBar = MenuBar.new # File Menu @m_pFileMenu = Menu.new @m_pFileMenu.append(MENU_FILE_OPEN, "&Open", "Opens an existing file") @m_pFileMenu.append(MENU_FILE_SAVE, "&Save", "Save the content") @m_pFileMenu.append_separator @m_pFileMenu.append(MENU_FILE_QUIT, "&Quit", "Quit the application") @m_pMenuBar.append(@m_pFileMenu, "&File") # Edit Menu @m_pEditMenu = Menu.new @m_pEditMenu.append(MENU_EDIT_GOTO, "&Goto Line", "Jump to a line") @m_pMenuBar.append(@m_pEditMenu, "&Edit") # Option Menu @m_pOptionMenu = Menu.new @m_pOptionMenu.append(MENU_OPTION_BACKGROUND, "&Background", "Sets the background colour of the editor") @m_pOptionMenu.append(MENU_OPTION_FONT, "&Font", "Sets the font of the editor") @m_pOptionMenu.append(MENU_OPTION_DIRECTORY, "&Directory", "Sets the working directory") @m_pMenuBar.append(@m_pOptionMenu, "&Options") # About menu @m_pInfoMenu = Menu.new @m_pInfoMenu.append(MENU_INFO_ABOUT, "&About", "Shows information about the application") @m_pMenuBar.append(@m_pInfoMenu, "&Info") set_menu_bar(@m_pMenuBar) # Statusbar create_status_bar(3) set_status_text("Ready", 0) evt_menu(MENU_FILE_OPEN) {|event| onMenuFileOpen(event) } evt_menu(MENU_FILE_SAVE) {|event| onMenuFileSave(event) } evt_menu(MENU_FILE_QUIT) {|event| onMenuFileQuit(event) } evt_menu(MENU_EDIT_GOTO) {|event| onMenuEditGoto(event) } evt_menu(MENU_INFO_ABOUT) {|event| onMenuInfoAbout(event) } evt_menu(MENU_OPTION_BACKGROUND) {|event| onMenuOptionBackgroundColor(event) } evt_menu(MENU_OPTION_FONT) {|event| onMenuOptionFont(event) } evt_menu(MENU_OPTION_DIRECTORY) {|event| onMenuOptionDirectory(event) } evt_close {|event| onClose(event) } end # def show(visible) # super(visible) # @m_pTextCtrl.set_focus # end def onMenuFileOpen(event) dlg = FileDialog.new(self, "Open a text file", "", "", "All files(*.*)|*.*|Text Files(*.txt)|*.txt", OPEN, DEFAULT_POSITION) if dlg.show_modal == ID_OK @m_pTextCtrl.load_file(dlg.get_filename) set_status_text(dlg.get_filename, 0) end dlg.destroy end def onMenuFileOpen(event) dlg = FileDialog.new(self, "Open a text file", "", "", "All files(*.*)|*.*|Text Files(*.txt)|*.txt", OPEN, DEFAULT_POSITION) if dlg.show_modal == ID_OK @m_pTextCtrl.load_file(dlg.get_filename) set_status_text(dlg.get_filename, 0) end dlg.destroy() end def onMenuFileSave(event) dlg = FileDialog.new(self, "Save a text file", "", "", "All files(*.*)|*.*|Text Files(*.txt)|*.txt", SAVE, DEFAULT_POSITION) if dlg.show_modal == ID_OK @m_pTextCtrl.save_file(dlg.get_path) set_status_text(dlg.get_filename, 0) end dlg.destroy @m_pTextCtrl.discard_edits end def onMenuFileQuit(event) close(FALSE) end def onMenuInfoAbout(event) dlg = AboutDialog.new(self) dlg.set_text("(c) 2001 S.A.W. Franky Braem\nSimple Text Editor\n") dlg.show_modal dlg.destroy end def onMenuEditGoto(event) dlg = GotoLineDialog.new(self) dlg.SetMaxLineNumber(@m_pTextCtrl.get_number_of_lines) if dlg.show_modal == ID_OK # Convert the linenumber to a position and set self as the new insertion point. #p dlg.GetLineNumber @m_pTextCtrl.set_insertion_point(@m_pTextCtrl.xy_to_position(0, dlg.GetLineNumber - 1)) end dlg.destroy end def onMenuOptionBackgroundColor(event) colourData = ColourData.new colour = @m_pTextCtrl.get_background_colour colourData.set_colour(colour) colourData.set_choose_full(true) dlg = ColourDialog.new(self, colourData) if dlg.show_modal == ID_OK colourData = dlg.get_colour_data @m_pTextCtrl.set_background_colour(colourData.get_colour) @m_pTextCtrl.refresh end dlg.destroy end def onMenuOptionFont(event) fontData = FontData.new font = @m_pTextCtrl.get_font fontData.set_initial_font(font) colour = @m_pTextCtrl.get_foreground_colour fontData.set_colour(colour) fontData.set_show_help(true) dlg = FontDialog.new(self, fontData) if dlg.show_modal == ID_OK fontData = dlg.get_font_data font = fontData.get_chosen_font @m_pTextCtrl.set_font(font) @m_pTextCtrl.set_foreground_colour(fontData.get_colour) @m_pTextCtrl.refresh end dlg.destroy end def onMenuOptionDirectory(event) dlg = DirDialog.new(self, "Select a Working directory", get_cwd()) if dlg.show_modal == ID_OK set_working_directory(dlg.get_path) end dlg.destroy end def onClose(event) destroy = true if event.can_veto if @m_pTextCtrl.is_modified dlg = MessageDialog.new(self, "Text is changed!\nAre you sure you want to exit?", "Text changed!!!", YES_NO | NO_DEFAULT) result = dlg.show_modal if result == ID_NO event.veto destroy = false end end end if destroy destroy() end end end class RbApp < App def on_init frame = TextFrame.new("Simple Text Editor", 100, 100, 400, 300) puts frame set_top_window(frame) frame.show(TRUE) end end a = RbApp.new a.main_loop
Roy Sutton wrote:> Attached are two files:Cool. I see the same problem you do. I don''t yet understand why swig_up appears to be incorrect in the case of validators. I tested swig_up in general by overriding on_exit in App, and using printfs in App.cpp to watch what happened. wx calls the director''s OnExit, which calls ruby''s on_exit, which I had invoke super, which went back into OnExit, and called the C++ version. Just like it was supposed to. My best guess (and it''s a somewhat wild guess at this point) is that somehow the chain of calls with validators is such that the global swig_up variable is being set somewhere else, and it''s affecting the validator code. I''ll look at it more later, when I have more time. Until then, all I can tell you is that you''re not imagining things, but that it''s not a universal problem with the swig_up mechanism. Kevin
My current theory after thinking on it some more was that what''s happening is that swig_up is turned off to prevent endless loops. So, after a single swig override the swig_up is turned off. So, calls that result in more than one override will fail on the 2nd+ override. What do you think? Roy Kevin Smith wrote:> Roy Sutton wrote: > >> Attached are two files: > > > Cool. I see the same problem you do. > > I don''t yet understand why swig_up appears to be incorrect in the case > of validators. I tested swig_up in general by overriding on_exit in > App, and using printfs in App.cpp to watch what happened. wx calls the > director''s OnExit, which calls ruby''s on_exit, which I had invoke > super, which went back into OnExit, and called the C++ version. Just > like it was supposed to. > > My best guess (and it''s a somewhat wild guess at this point) is that > somehow the chain of calls with validators is such that the global > swig_up variable is being set somewhere else, and it''s affecting the > validator code. > > I''ll look at it more later, when I have more time. Until then, all I > can tell you is that you''re not imagining things, but that it''s not a > universal problem with the swig_up mechanism. > > Kevin > _______________________________________________ > wxruby-users mailing list > wxruby-users@rubyforge.org > http://rubyforge.org/mailman/listinfo/wxruby-users > > >
Roy Sutton wrote:> My current theory after thinking on it some more was that what''s > happening is that swig_up is turned off to prevent endless loops. So, > after a single swig override the swig_up is turned off. So, calls that > result in more than one override will fail on the 2nd+ override. What > do you think?I was thinking something like that, but when swig_up is false, calls get routed to ruby, not C++. We should be seeing too many calls going into ruby and not enough into C++. But in this case, the call is getting routed to C++ when it should go to ruby. My current (untested) theory is similar, however. You could test it with code something like this: C++ ----------- class TestSwigUp { public: virtual void virt1() { nonvirt1(); } void nonvirt1() { virt2(); } virtual void virt2() { printf("C++ virt2\n"); } }; Ruby ------------ class RubyTestSwigUp < TestSwigUp def initialize virt1 end def virt2 puts("ruby virt2") end end I''m thinking that the call to virt1 will go into the director''s virt1, which will set swig_up and then call C++ virt1, which will call the non-director wrap_nonvirt1, which will call the C++ nonvirt1, which will call the director''s virt2. At that point, swig_up is still set from earlier, so it will call the C++ virt2 instead of the ruby virt2 that we wanted. This corresponds to your real world case where the C++ clone was being called instead of the ruby clone. You could probably test this within wxruby2 by using %extend and adding those three methods to an existing class (such as App). Instead, you might be able to add this class to an existing .i file (but I can imagine some possible problems) or add a new .i file with this class (but I can imagine some other problems). Finally, you could test this entirely outside wxruby2 just as a simple swig sample, which might be best for reporting it to the swig maintainers, if it does turn out to be a problem. If this is a problem, it''s hard to imagine a fix. Non-virtual methods can''t be hooked by swig, so I can''t think of how to detect this condition. But the first step is to figure out whether this is really the problem or not. Then, we can talk with Robin (wxPython), Lyle (FXRuby/swigruby), and other folks about how they have worked around it. Thanks, Kevin
Kevin Smith wrote:> I''m thinking that the call to virt1 will go into the director''s virt1, > which will set swig_up and then call C++ virt1, which will call the > non-director wrap_nonvirt1, which will call the C++ nonvirt1, which will > call the director''s virt2. > > At that point, swig_up is still set from earlier, so it will call the > C++ virt2 instead of the ruby virt2 that we wanted. This corresponds to > your real world case where the C++ clone was being called instead of the > ruby clone.I just tested this, and my theory was wrong. The ruby version of virt2 was called, as one would hope. Turns out swig_up is only turned on very briefly. That is, when a wrap_ method is called (which can only happen from Ruby), the flag is set, and the call is forwarded to the director''s implementation of that method. The director checks the flag (which clears it), and because it is set it routes the call to the director''s superclass, which is the original wrapped C++ code. The only way swig_up will get set again is if ruby code calls another wrapped director method. In short, all the logic looks right in my little sample. I''m tempted to check it in as part of the project, just to have a test platform for further research. Currently it consists of wxBogus.h, Bogus.i, and testobgus.rb. I''m SOOO happy that just adding a .h/.i brings a whole new class into wxruby2, without having to modify any other files! Should I check it in? Kevin
Roy Sutton wrote:> Try running test21.rb. Open the Edit->''Goto Line'' dialog box. > > You -should- see ''Validator.Clone was called'' followed by > ''Validator.Copy was called'' in the console. But you won''t if it behaves > like it does on mine. You can go into Validator.cpp and add a printf > into the Clone wrapper and you''ll see swig_up is false.I spent more time with this, and I now suspect the problem might have something to do with copy constructors. The first thing I did was to heavily edit test21.rb down to be a smaller app. Any clutter (such as the sizers that weren''t working) could affect the results. I would recommend creating a trivial app that just creates a single control and tries to attach to it a very minimal validator. I also removed the %ignore of Window#get_validator, and checked that in. The docs say that any Validator subclass *must* implement a copy constructor. In C++, if you don''t define one, one will be defined for you...and it almost never does what you want, because it''s a straight bit-copy. Validator.h does not define a copy constructor. However, based on more digging, my earlier theory is looking better again. We are calling Window#set_validator, which sets swig_up, and calls the C++ version, which calls Validator::Clone, but swig_up is still set, so it goes to the C++ version instead of to the Ruby version. I thought that''s what I was testing in my Bogus sample, but it must be slightly different somehow. The next step might be to implement a copy constructor. To do that, I think we''ll need to define a subclass in Validator.i, which will end up similar to (but simpler than) App.i. The docs mention that wxPython has a wxPyValidator class, and they recommend that you subclass that to get cool python features. That might also be part of their solution for the copy constructor problem. Another thing that would help this case, but probably not globally, would be to make swig_up static, instead of global. I think it was static in swig 1.3.22. Kevin
Well, I''m almost disappointed it works correctly. :) I suppose I''ll have to look into it some more. As to whether this should be in the repository or not... I suppose I''d be happier if we could make a test case that craps out so that we can have a unit test that ensure it stays fixed. That being said, if you''d like to forward those files to the list I''ll plug ''em in here and try ''em out. Roy Kevin Smith wrote:> Kevin Smith wrote: > >> I''m thinking that the call to virt1 will go into the director''s >> virt1, which will set swig_up and then call C++ virt1, which will >> call the non-director wrap_nonvirt1, which will call the C++ >> nonvirt1, which will call the director''s virt2. >> >> At that point, swig_up is still set from earlier, so it will call the >> C++ virt2 instead of the ruby virt2 that we wanted. This corresponds >> to your real world case where the C++ clone was being called instead >> of the ruby clone. > > > I just tested this, and my theory was wrong. The ruby version of virt2 > was called, as one would hope. > > Turns out swig_up is only turned on very briefly. That is, when a > wrap_ method is called (which can only happen from Ruby), the flag is > set, and the call is forwarded to the director''s implementation of > that method. The director checks the flag (which clears it), and > because it is set it routes the call to the director''s superclass, > which is the original wrapped C++ code. The only way swig_up will get > set again is if ruby code calls another wrapped director method. > > In short, all the logic looks right in my little sample. I''m tempted > to check it in as part of the project, just to have a test platform > for further research. Currently it consists of wxBogus.h, Bogus.i, and > testobgus.rb. I''m SOOO happy that just adding a .h/.i brings a whole > new class into wxruby2, without having to modify any other files! > > Should I check it in? > > Kevin > _______________________________________________ > wxruby-users mailing list > wxruby-users@rubyforge.org > http://rubyforge.org/mailman/listinfo/wxruby-users > > >
I took at look at the Python implementation of wxPyValidator. They must have lots of fancy swig support because there was a lot of swig stuff I haven''t seen before. Either that or they have some macros they use embedded somewhere. I didn''t go chase it down. It would be pretty trivial to make a class that derives from Validator. I''m just not sure yet if it''s the way to go. I''d like to investigate it some more. Kevin Smith wrote:> Roy Sutton wrote: > >> Try running test21.rb. Open the Edit->''Goto Line'' dialog box. >> >> You -should- see ''Validator.Clone was called'' followed by >> ''Validator.Copy was called'' in the console. But you won''t if it >> behaves like it does on mine. You can go into Validator.cpp and add >> a printf into the Clone wrapper and you''ll see swig_up is false. > > > > I spent more time with this, and I now suspect the problem might have > something to do with copy constructors. The first thing I did was to > heavily edit test21.rb down to be a smaller app. Any clutter (such as > the sizers that weren''t working) could affect the results. I would > recommend creating a trivial app that just creates a single control > and tries to attach to it a very minimal validator. > > I also removed the %ignore of Window#get_validator, and checked that in. > > The docs say that any Validator subclass *must* implement a copy > constructor. In C++, if you don''t define one, one will be defined for > you...and it almost never does what you want, because it''s a straight > bit-copy. Validator.h does not define a copy constructor. > > However, based on more digging, my earlier theory is looking better > again. We are calling Window#set_validator, which sets swig_up, and > calls the C++ version, which calls Validator::Clone, but swig_up is > still set, so it goes to the C++ version instead of to the Ruby > version. I thought that''s what I was testing in my Bogus sample, but > it must be slightly different somehow. > > The next step might be to implement a copy constructor. To do that, I > think we''ll need to define a subclass in Validator.i, which will end > up similar to (but simpler than) App.i. > > The docs mention that wxPython has a wxPyValidator class, and they > recommend that you subclass that to get cool python features. That > might also be part of their solution for the copy constructor problem. > > Another thing that would help this case, but probably not globally, > would be to make swig_up static, instead of global. I think it was > static in swig 1.3.22. > > Kevin
Kevin Smith wrote:> However, based on more digging, my earlier theory is looking better > again. We are calling Window#set_validator, which sets swig_up, and > calls the C++ version, which calls Validator::Clone, but swig_up is > still set, so it goes to the C++ version instead of to the Ruby version. > I thought that''s what I was testing in my Bogus sample, but it must be > slightly different somehow.Aha! I believe I have found the problem. The ruby code calls _wrap_wxWindow_SetValidator, which sets swig_up and then invokes the C++ SetValidator. BUT the actual object is a wxTextCtrl, not a wxWindow. So we bypass SwigDirector_wxWindow::SetValidator, which would have cleared swig_up. Instead we directly invoke wxTextCtrl::SetValidator, which falls back to wxWindow::SetValidator. That invokes Clone on our Validator object, which goes into SwigDirector_wxValidator::Clone, which sees that swig_up is still set, and calls the C++ clone instead of the ruby clone. The real problem is that a director method wasn''t created for wxTextCtrl::SetValidator. That''s a derived class and a virtual method, so according to the swig python docs[1], it should have gotten a director. I can think of a few possible reasons why we might not be getting these directors: 1. We put each class in a separate swig module, so swig isn''t following the inheritance chain up to look for virtual methods. 2. We are missing a %feature or other swig directive. Our common.i asks for global directors, so this seems less likely. 3. Director support in ruby is not as full as in python. The docs imply that this is not the case, and that ruby has full director features[2]. Kevin [1] http://www.swig.org/Doc1.3/Python.html#Python_nn33 [2] http://www.swig.org/Doc1.3/Ruby.html#Ruby_nn25
Kevin, This is what I discovered last night as well. This happens all over the place as you can imagine. We are not generating any wrappers for virtual functions not defined in the subclasses. swig has an option to go through all the header files included... Do we need to do this? Here is some output from my test version of wxRuby2: swig_up=false in src/App.cpp:1309 setting up in src/Frame.cpp:2177 swig_up=true in src/Frame.cpp:1192 swig_up=false in src/Frame.cpp:1171 setting up in src/Frame.cpp:2697 swig_up=true in src/Frame.cpp:1171 setting up in src/Frame.cpp:2853 swig_up=true in src/Frame.cpp:1248 setting up in src/Window.cpp:6966 <-- Swig up not cleared in subclass setting up in src/StaticText.cpp:1913 swig_up=true in src/StaticText.cpp:1148 setting up in src/Window.cpp:6593 <-- Swig up not cleared in subclass swig_up=true in src/Validator.cpp:1156 setting up in src/Window.cpp:7080 setting up in src/Window.cpp:5366 <-- Swig up not cleared in subclass setting up in src/Window.cpp:3145 <-- Swig up not cleared in subclass swig_up=true in src/App.cpp:1283 setting up in src/Window.cpp:3145 <-- Swig up not cleared in subclass swig_up=true in src/App.cpp:1283 swig_up=false in src/App.cpp:1296 setting up in src/App.cpp:1645 swig_up=true in src/App.cpp:1296 Kevin Smith wrote:> Kevin Smith wrote: > >> However, based on more digging, my earlier theory is looking better >> again. We are calling Window#set_validator, which sets swig_up, and >> calls the C++ version, which calls Validator::Clone, but swig_up is >> still set, so it goes to the C++ version instead of to the Ruby >> version. I thought that''s what I was testing in my Bogus sample, but >> it must be slightly different somehow. > > > Aha! I believe I have found the problem. The ruby code calls > _wrap_wxWindow_SetValidator, which sets swig_up and then invokes the > C++ SetValidator. BUT the actual object is a wxTextCtrl, not a > wxWindow. So we bypass SwigDirector_wxWindow::SetValidator, which > would have cleared swig_up. Instead we directly invoke > wxTextCtrl::SetValidator, which falls back to wxWindow::SetValidator. > That invokes Clone on our Validator object, which goes into > SwigDirector_wxValidator::Clone, which sees that swig_up is still set, > and calls the C++ clone instead of the ruby clone. > > The real problem is that a director method wasn''t created for > wxTextCtrl::SetValidator. That''s a derived class and a virtual method, > so according to the swig python docs[1], it should have gotten a > director. I can think of a few possible reasons why we might not be > getting these directors: > > 1. We put each class in a separate swig module, so swig isn''t > following the inheritance chain up to look for virtual methods. > > 2. We are missing a %feature or other swig directive. Our common.i > asks for global directors, so this seems less likely. > > 3. Director support in ruby is not as full as in python. The docs > imply that this is not the case, and that ruby has full director > features[2]. > > Kevin > > > [1] http://www.swig.org/Doc1.3/Python.html#Python_nn33 > [2] http://www.swig.org/Doc1.3/Ruby.html#Ruby_nn25 > _______________________________________________ > wxruby-users mailing list > wxruby-users@rubyforge.org > http://rubyforge.org/mailman/listinfo/wxruby-users > > >
I checked out the -includeall option. This just won''t work for us. The bottom line appears to be there is no good method short of pre-processing the included files to extract all virtual functions and create stubs that just call the inherited base class. Of course, if there''s a way to specify the -includeall -and- specify that it shouldn''t wrap anything except a specified object or the base include file... Roy Roy Sutton wrote:> This is what I discovered last night as well. This happens all over > the place as you can imagine. We are not generating any wrappers for > virtual functions not defined in the subclasses. swig has an option > to go through all the header files included... Do we need to do this?
Roy Sutton wrote:> Kevin, > > This is what I discovered last night as well. This happens all over the > place as you can imagine. We are not generating any wrappers for > virtual functions not defined in the subclasses.Yup. I just checked in the tiny start of a fix for this. I added %import statements in TextCtrl.i and Control.i so that swig knows all about all the parent classes. That allows it to create all the Director methods for any virtual methods that were defined up the inheritance chain. You can confirm this by looking at src/TextCtrl.h after re-swigging. I was also able to get much farther into your test21.rb sample, with results that also seemed to confirm that this solves that problem. We will need to add %imports for *all* the classes. Some of the %imports will fail, and hopefully most if not all of them can be fixed by moving the conditional logic from the .i files to the .h files, as I just did with Window. I''m going to apply the patches in my queue (one from you and one from Sean, it looks like), and then I''ll tag 0.0.25. After that, I''ll start adding %imports like crazy, until all the classes are covered. If anyone wants to volunteer to help with this, let me know and we can split the classes up into different sections and work without colliding. At least there is a solution, and it''s not terribly painful. Kevin
Kevin Smith wrote:> After that, I''ll start > adding %imports like crazy, until all the classes are covered. If anyone > wants to volunteer to help with this, let me know and we can split the > classes up into different sections and work without colliding.I just committed all the %imports, so all the classes should have good inheritance chains now, and all virtual methods sould be overrideable in Ruby. Most likely, there are some errors, either due to incorrect wx docs, or to my own mistakes. Hopefully, not very many. Kevin
Great work Kevin! One thing this has surfaced is inconsitencies in the header files. Attached is a patched wxDialog.h. I tried to catch all the virtual functions that weren''t marked as virtual. Roy Kevin Smith wrote:> Kevin Smith wrote: > >> After that, I''ll start adding %imports like crazy, until all the >> classes are covered. If anyone wants to volunteer to help with this, >> let me know and we can split the classes up into different sections >> and work without colliding. > > > I just committed all the %imports, so all the classes should have good > inheritance chains now, and all virtual methods sould be overrideable > in Ruby. > > Most likely, there are some errors, either due to incorrect wx docs, > or to my own mistakes. Hopefully, not very many. > > Kevin > _______________________________________________ > wxruby-users mailing list > wxruby-users@rubyforge.org > http://rubyforge.org/mailman/listinfo/wxruby-users > > >-------------- next part -------------- --- wxruby2_old\swig\classes\include\wxDialog.h 2005-01-30 22:18:52.000000000 -0500 +++ wxruby2\swig\classes\include\wxDialog.h 2005-08-21 12:56:42.851758400 -0400 @@ -55,7 +55,7 @@ * \param int */ - void EndModal(int retCode ) ; + virtual void EndModal(int retCode ) ; /** * \brief */ @@ -81,7 +81,7 @@ * \brief Returns true if the dialog box is modal, false otherwise. */ - bool IsModal() const; + virtual bool IsModal() const; /** * \brief This member is called to allow the window to intercept keyboard events before they are processed by child windows. @@ -164,13 +164,13 @@ * \param const bool */ - bool Show(const bool show ) ; + virtual bool Show(bool show = true ) ; /** * \brief Shows a modal dialog. Program flow does not return until the dialog has been dismissed with . */ - int ShowModal() ; + virtual int ShowModal() ; };
Roy Sutton wrote:> Great work Kevin! One thing this has surfaced is inconsitencies in the > header files. Attached is a patched wxDialog.h. I tried to catch all > the virtual functions that weren''t marked as virtual.Committed. This patch seemed to *almost* work. The only problem with it appeared to be that the paths have backslashes instead of slashes. When ''patch'' prompted me for the filename, I entered it with forward slashes, and it applied correctly! I suspect we will find *A LOT* of methods that should be marked virtual throughout the system. The original wx docs from which those .h files were generated unfortunately had a lot of errors. Thanks, Kevin> > Roy > > Kevin Smith wrote: > >> Kevin Smith wrote: >> >>> After that, I''ll start adding %imports like crazy, until all the >>> classes are covered. If anyone wants to volunteer to help with this, >>> let me know and we can split the classes up into different sections >>> and work without colliding. >> >> >> >> I just committed all the %imports, so all the classes should have good >> inheritance chains now, and all virtual methods sould be overrideable >> in Ruby. >> >> Most likely, there are some errors, either due to incorrect wx docs, >> or to my own mistakes. Hopefully, not very many. >> >> Kevin >> _______________________________________________ >> wxruby-users mailing list >> wxruby-users@rubyforge.org >> http://rubyforge.org/mailman/listinfo/wxruby-users >> >> >> > > ------------------------------------------------------------------------ > > --- wxruby2_old\swig\classes\include\wxDialog.h 2005-01-30 22:18:52.000000000 -0500 > +++ wxruby2\swig\classes\include\wxDialog.h 2005-08-21 12:56:42.851758400 -0400 > @@ -55,7 +55,7 @@ > * \param int > */ > > - void EndModal(int retCode ) ; > + virtual void EndModal(int retCode ) ; > /** > * \brief > */ > @@ -81,7 +81,7 @@ > * \brief Returns true if the dialog box is modal, false otherwise. > */ > > - bool IsModal() const; > + virtual bool IsModal() const; > /** > * \brief This member is called to allow the window to intercept keyboard events > before they are processed by child windows. > @@ -164,13 +164,13 @@ > * \param const bool > */ > > - bool Show(const bool show ) ; > + virtual bool Show(bool show = true ) ; > /** > * \brief Shows a modal dialog. Program flow does not return until the dialog has been dismissed with > . > */ > > - int ShowModal() ; > + virtual int ShowModal() ; > }; > > > > > ------------------------------------------------------------------------ > > _______________________________________________ > wxruby-users mailing list > wxruby-users@rubyforge.org > http://rubyforge.org/mailman/listinfo/wxruby-users
Maybe Matching Threads
- Deploying libvirt with live migration
- Centos8: Glusterd do not start correctly when I startup or reboot all server together
- unable to remove brick, pleas help
- grep contents of file on remote server
- [778] trunk/wxruby2: Remove broken and deprecated LayoutConstraints, update samples & docs