Troubleshooting sip assignHelper != NULL assertion
Scott Talbert
swt at techie.net
Sun May 31 17:48:46 BST 2020
On Sun, 31 May 2020, Phil Thompson wrote:
> On 31/05/2020 16:29, Scott Talbert wrote:
>> On Sun, 31 May 2020, Phil Thompson wrote:
>>
>>> On 31/05/2020 01:23, Scott Talbert wrote:
>>>> On Sat, 30 May 2020, Phil Thompson wrote:
>>>>
>>>>> On 28/05/2020 16:52, Scott Talbert wrote:
>>>>>> On Thu, 28 May 2020, Phil Thompson wrote:
>>>>>>
>>>>>>> On 28/05/2020 16:30, Scott Talbert wrote:
>>>>>>>> On Thu, 28 May 2020, Phil Thompson wrote:
>>>>>>>>
>>>>>>>>>>>> Hi,
>>>>>>>>>>>>
>>>>>>>>>>>> I'm running into the following assertion in wxPython that I
>>>>>>>>>>>> don't
>>>>>>>>>>>> quite understand:
>>>>>>>>>>>>
>>>>>>>>>>>> python3: ../../../../sip/siplib/siplib.c:3444: parseResult:
>>>>>>>>>>>> Assertion
>>>>>>>>>>>> `assign_helper != NULL' failed.
>>>>>>>>>>>>
>>>>>>>>>>>> This is the relevant C++ class:
>>>>>>>>>>>>
>>>>>>>>>>>> class wxPGWindowList
>>>>>>>>>>>> {
>>>>>>>>>>>> public:
>>>>>>>>>>>> wxPGWindowList(wxWindow* primary, wxWindow* secondary =
>>>>>>>>>>>> NULL)
>>>>>>>>>>>> : m_primary(primary)
>>>>>>>>>>>> , m_secondary(secondary)
>>>>>>>>>>>> {
>>>>>>>>>>>> }
>>>>>>>>>>>>
>>>>>>>>>>>> void SetSecondary(wxWindow* secondary) { m_secondary =
>>>>>>>>>>>> secondary; }
>>>>>>>>>>>>
>>>>>>>>>>>> wxWindow* GetPrimary() const { return m_primary; }
>>>>>>>>>>>> wxWindow* GetSecondary() const { return m_secondary; }
>>>>>>>>>>>>
>>>>>>>>>>>> wxWindow* m_primary;
>>>>>>>>>>>> wxWindow* m_secondary;
>>>>>>>>>>>> };
>>>>>>>>>>>
>>>>>>>>>>> It doesn't matter what the C++ class looks like, it's the
>>>>>>>>>>> corresponding .sip that would be of interest.
>>>>>>>>>>
>>>>>>>>>> My bad, here it is:
>>>>>>>>>> class wxPGWindowList
>>>>>>>>>> {
>>>>>>>>>> %Docstring
>>>>>>>>>> PGWindowList(primary, secondary=None)
>>>>>>>>>>
>>>>>>>>>> Contains a list of editor windows returned by
>>>>>>>>>> CreateControls.
>>>>>>>>>> %End
>>>>>>>>>> %TypeHeaderCode
>>>>>>>>>> #include <wx/propgrid/editors.h>
>>>>>>>>>> %End
>>>>>>>>>>
>>>>>>>>>> public:
>>>>>>>>>> wxPGWindowList(
>>>>>>>>>> wxWindow * primary,
>>>>>>>>>> wxWindow * secondary = NULL
>>>>>>>>>> );
>>>>>>>>>>
>>>>>>>>>> void SetSecondary(
>>>>>>>>>> wxWindow * secondary
>>>>>>>>>> );
>>>>>>>>>> %Docstring
>>>>>>>>>> SetSecondary(secondary)
>>>>>>>>>> %End
>>>>>>>>>>
>>>>>>>>>> wxWindow * GetPrimary() const;
>>>>>>>>>> %Docstring
>>>>>>>>>> GetPrimary() -> wx.Window
>>>>>>>>>>
>>>>>>>>>> Gets window of primary editor.
>>>>>>>>>> %End
>>>>>>>>>>
>>>>>>>>>> wxWindow * GetSecondary() const;
>>>>>>>>>> %Docstring
>>>>>>>>>> GetSecondary() -> wx.Window
>>>>>>>>>>
>>>>>>>>>> Gets window of secondary editor.
>>>>>>>>>> %End
>>>>>>>>>>
>>>>>>>>>> public:
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> %Property(name=Primary, get=GetPrimary)
>>>>>>>>>> %Property(name=Secondary, get=GetSecondary, set=SetSecondary)
>>>>>>>>>> }; // end of class wxPGWindowList
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>>> The assertion occurs when trying to return an instance of
>>>>>>>>>>>> wxPGWindowList in a Python method, e.g.:
>>>>>>>>>>>>
>>>>>>>>>>>> def foo():
>>>>>>>>>>>> return wxpg.PGWindowList(a, b)
>>>>>>>>>>>>
>>>>>>>>>>>> From what I can tell, there is no assignment helper assigned by
>>>>>>>>>>>> sip
>>>>>>>>>>>> because there is no default constructor? I may be missing
>>>>>>>>>>>> something,
>>>>>>>>>>>> but I can't see why a default constructor would be needed.
>>>>>>>>>>>
>>>>>>>>>>> You don't say what version of the sip module you are using but
>>>>>>>>>>> I'm guessing that that assertion is when it's parsing the Python
>>>>>>>>>>> object returned by a re-implementation of a C++ virtual. That
>>>>>>>>>>> doesn't seem to be happening in the above which suggests things
>>>>>>>>>>> aren't happening where you think they are.
>>>>>>>>>>
>>>>>>>>>> sip module version is 4.19.19. You are correct, I oversimplified
>>>>>>>>>> what
>>>>>>>>>> is actually happening. This is really what is happening:
>>>>>>>>>>
>>>>>>>>>> class LargeImageEditor(wxpg.PGEditor):
>>>>>>>>>> def CreateControls(self, propgrid, property, pos, sz):
>>>>>>>>>> ...
>>>>>>>>>> return wxpg.PGWindowList(self.tc, btn)
>>>>>>>>>>
>>>>>>>>>> Where CreateControls is a C++ virtual, relevant .sip snippet:
>>>>>>>>>>
>>>>>>>>>> virtual
>>>>>>>>>> wxPGWindowList CreateControls(
>>>>>>>>>> wxPropertyGrid * propgrid,
>>>>>>>>>> wxPGProperty * property,
>>>>>>>>>> const wxPoint & pos,
>>>>>>>>>> const wxSize & size
>>>>>>>>>> ) const = 0;
>>>>>>>>>> %Docstring
>>>>>>>>>> CreateControls(propgrid, property, pos, size) ->
>>>>>>>>>> PGWindowList
>>>>>>>>>>
>>>>>>>>>> Instantiates editor controls.
>>>>>>>>>> %End
>>>>>>>>>
>>>>>>>>> So SIP need to copy the wxPGWindowList from the stack to the heap.
>>>>>>>>> Shouldn't CreateControls return a pointer to the wxPGWindowList?
>>>>>>>>>
>>>>>>>>> Of course SIP should detect this when generating the code rather
>>>>>>>>> than rely on a runtime assertion.
>>>>>>>>
>>>>>>>> That is probably how I would have designed the API, but alas it
>>>>>>>> wasn't
>>>>>>>> done that way. :)
>>>>>>>>
>>>>>>>> Shouldn't a (default) copy constructor be sufficient to copy the
>>>>>>>> wxPGWindowList in this case?
>>>>>>>
>>>>>>> SIP may be being too conservative when determining if there is an
>>>>>>> implied copy ctor. Try adding an explicit copy ctor to the
>>>>>>> wxPGWindowList .sip file.
>>>>>>
>>>>>> I don't think that will be enough, though. SIP seems to only set the
>>>>>> assignment helper if there are *both* a public default constructor and
>>>>>> a public copy constructor, see:
>>>>>>
>>>>>> https://www.riverbankcomputing.com/hg/sip/file/d85e9957e726/sipgen/transform.c#l596
>>>>>>
>>>>>> Scott
>>>>>
>>>>> Should be fixed in the current repo. SIP v4.19.23 will be released next
>>>>> week.
>>>>>
>>>>> Be aware that the fix may expose missing private ctors in other .sip
>>>>> files - PyQt had two cases.
>>>>
>>>> Thanks for the quick fix.
>>>>
>>>> However, I think I found a situation with the new code where an assign
>>>> function is getting created where it probably shouldn't be.
>>>>
>>>> struct wxSplitterRenderParams
>>>> {
>>>> %Docstring
>>>> SplitterRenderParams(widthSash_, border_, isSens_)
>>>>
>>>> This is just a simple struct used as a return value of
>>>> wxRendererNative::GetSplitterParams().
>>>> %End
>>>> %TypeHeaderCode
>>>> #include <wx/renderer.h>
>>>> %End
>>>>
>>>> wxSplitterRenderParams(
>>>> wxCoord widthSash_,
>>>> wxCoord border_,
>>>> bool isSens_
>>>> );
>>>>
>>>> const wxCoord border;
>>>>
>>>> const bool isHotSensitive;
>>>>
>>>> const wxCoord widthSash;
>>>>
>>>> }; // end of class wxSplitterRenderParams
>>>>
>>>> This results in:
>>>>
>>>> ../../../../sip/cpp/sip_corewxSplitterRenderParams.cpp: In function
>>>> ‘void assign_wxSplitterRenderParams(void*, Py_ssize_t, void*)’:
>>>> ../../../../sip/cpp/sip_corewxSplitterRenderParams.cpp:31:125: error:
>>>> use of deleted function ‘wxSplitterRenderParams&
>>>> wxSplitterRenderParams::operator=(const wxSplitterRenderParams&)’
>>>> 31 | reinterpret_cast< ::wxSplitterRenderParams
>>>> *>(sipDst)[sipDstIdx] = *reinterpret_cast< ::wxSplitterRenderParams
>>>> *>(sipSrc);
>>>> | ^
>>>> In file included from
>>>> ../../../../sip/cpp/sip_corewxSplitterRenderParams.cpp:12:
>>>> ../../../../../Phoenix/ext/wxWidgets/include/wx/renderer.h:97:25:
>>>> note: ‘wxSplitterRenderParams& wxSplitterRenderParams::operator=(const
>>>> wxSplitterRenderParams&)’ is implicitly deleted because the default
>>>> definition would be ill-formed:
>>>> 97 | struct WXDLLIMPEXP_CORE wxSplitterRenderParams
>>>> | ^~~~~~~~~~~~~~~~~~~~~~
>>>> ../../../../../Phoenix/ext/wxWidgets/include/wx/renderer.h:97:25:
>>>> error: non-static const member ‘const wxCoord
>>>> wxSplitterRenderParams::widthSash’, cannot use default assignment
>>>> operator
>>>> ../../../../../Phoenix/ext/wxWidgets/include/wx/renderer.h:97:25:
>>>> error: non-static const member ‘const wxCoord
>>>> wxSplitterRenderParams::border’, cannot use default assignment
>>>> operator
>>>> ../../../../../Phoenix/ext/wxWidgets/include/wx/renderer.h:97:25:
>>>> error: non-static const member ‘const bool
>>>> wxSplitterRenderParams::isHotSensitive’, cannot use default assignment
>>>> operator
>>>
>>> Yes, that's what I meant by missing private copy ctors. SIP cannot
>>> accurately determine whether a copy ctor is available unless it knows
>>> everything about the C++ class (including private members and their
>>> types), but SIP is not a full C++ parser (and never will be). Therefore
>>> you need to declare a private copy ctor in the .sip file to correct the
>>> assumption that there is a public copy ctor available. In the future I
>>> may add a class annotation to achieve the same thing.
>>
>> Thanks, I understand that now. :) After declaring a private copy
>> ctor in the above .sip file, I'm now getting this:
>>
>> ../../../../sip/cpp/sip_corewxDelegateRendererNative.cpp: In function
>> ‘PyObject* meth_wxDelegateRendererNative_GetSplitterParams(PyObject*,
>> PyObject*, PyObject*)’:
>> ../../../../sip/cpp/sip_corewxDelegateRendererNative.cpp:1360:38:
>> error: taking address of rvalue [-fpermissive]
>> 1360 | sipRes = &(sipSelfWasArg ? sipCpp->
>> ::wxDelegateRendererNative::GetSplitterParams(win) :
>> sipCpp->GetSplitterParams(win));
>> |
>> ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>
>> So, since SIP can't copy the wxSplitterRenderParams, it is trying to
>> return a reference to the original one, but it seems that doesn't
>> work?
>
> So what does the .sip file for wxDelegateRenderNative look like?
It's a bit long, so attached.
Thanks,
Scott
-------------- next part --------------
class wxDelegateRendererNative : wxRendererNative
{
%Docstring
DelegateRendererNative()
DelegateRendererNative(rendererNative)
wxDelegateRendererNative allows reuse of renderers code by forwarding
all the wxRendererNative methods to the given object and thus allowing
you to only modify some of its methods without having to reimplement
all of them.
%End
%TypeHeaderCode
#include <wx/renderer.h>
%End
public:
wxDelegateRendererNative();
%PreMethodCode
if (!wxPyCheckForApp()) return NULL;
%End
wxDelegateRendererNative(
wxRendererNative & rendererNative
);
%PreMethodCode
if (!wxPyCheckForApp()) return NULL;
%End
virtual
int DrawHeaderButton(
wxWindow * win,
wxDC & dc,
const wxRect & rect,
int flags = 0,
wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE,
wxHeaderButtonParams * params = NULL
);
%Docstring
DrawHeaderButton(win, dc, rect, flags=0, sortArrow=HDR_SORT_ICON_NONE, params=None) -> int
Draw the header control button (used, for example, by wxListCtrl).
%End
virtual
int DrawHeaderButtonContents(
wxWindow * win,
wxDC & dc,
const wxRect & rect,
int flags = 0,
wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE,
wxHeaderButtonParams * params = NULL
);
%Docstring
DrawHeaderButtonContents(win, dc, rect, flags=0, sortArrow=HDR_SORT_ICON_NONE, params=None) -> int
Draw the contents of a header control button (label, sort arrows,
etc.).
%End
virtual
int GetHeaderButtonHeight(
wxWindow * win
);
%Docstring
GetHeaderButtonHeight(win) -> int
Returns the height of a header button, either a fixed platform height
if available, or a generic height based on the win window's font.
%End
virtual
int GetHeaderButtonMargin(
wxWindow * win
);
%Docstring
GetHeaderButtonMargin(win) -> int
Returns the horizontal margin on the left and right sides of header
button's label.
%End
virtual
void DrawTreeItemButton(
wxWindow * win,
wxDC & dc,
const wxRect & rect,
int flags = 0
);
%Docstring
DrawTreeItemButton(win, dc, rect, flags=0)
Draw the expanded/collapsed icon for a tree control item.
%End
virtual
void DrawSplitterBorder(
wxWindow * win,
wxDC & dc,
const wxRect & rect,
int flags = 0
);
%Docstring
DrawSplitterBorder(win, dc, rect, flags=0)
Draw the border for sash window: this border must be such that the
sash drawn by DrawSplitterSash() blends into it well.
%End
virtual
void DrawSplitterSash(
wxWindow * win,
wxDC & dc,
const wxSize & size,
wxCoord position,
wxOrientation orient,
int flags = 0
);
%Docstring
DrawSplitterSash(win, dc, size, position, orient, flags=0)
Draw a sash.
%End
virtual
void DrawComboBoxDropButton(
wxWindow * win,
wxDC & dc,
const wxRect & rect,
int flags = 0
);
%Docstring
DrawComboBoxDropButton(win, dc, rect, flags=0)
Draw a button like the one used by wxComboBox to show a drop down
window.
%End
virtual
void DrawDropArrow(
wxWindow * win,
wxDC & dc,
const wxRect & rect,
int flags = 0
);
%Docstring
DrawDropArrow(win, dc, rect, flags=0)
Draw a drop down arrow that is suitable for use outside a combo box.
%End
virtual
void DrawCheckBox(
wxWindow * win,
wxDC & dc,
const wxRect & rect,
int flags = 0
);
%Docstring
DrawCheckBox(win, dc, rect, flags=0)
Draw a check box.
%End
virtual
void DrawCheckMark(
wxWindow * win,
wxDC & dc,
const wxRect & rect,
int flags = 0
);
%Docstring
DrawCheckMark(win, dc, rect, flags=0)
Draw a check mark.
%End
virtual
wxSize GetCheckBoxSize(
wxWindow * win,
int flags = 0
);
%Docstring
GetCheckBoxSize(win, flags=0) -> Size
Returns the size of a check box.
%End
virtual
wxSize GetCheckMarkSize(
wxWindow * win
);
%Docstring
GetCheckMarkSize(win) -> Size
Returns the size of a check mark.
%End
virtual
wxSize GetExpanderSize(
wxWindow * win
);
%Docstring
GetExpanderSize(win) -> Size
Returns the size of the expander used in tree-like controls.
%End
virtual
void DrawPushButton(
wxWindow * win,
wxDC & dc,
const wxRect & rect,
int flags = 0
);
%Docstring
DrawPushButton(win, dc, rect, flags=0)
Draw a blank push button that looks very similar to wxButton.
%End
virtual
void DrawItemSelectionRect(
wxWindow * win,
wxDC & dc,
const wxRect & rect,
int flags = 0
);
%Docstring
DrawItemSelectionRect(win, dc, rect, flags=0)
Draw a selection rectangle underneath the text as used e.g.
%End
virtual
void DrawFocusRect(
wxWindow * win,
wxDC & dc,
const wxRect & rect,
int flags = 0
);
%Docstring
DrawFocusRect(win, dc, rect, flags=0)
Draw a focus rectangle using the specified rectangle.
%End
virtual
wxSplitterRenderParams GetSplitterParams(
const wxWindow * win
);
%Docstring
GetSplitterParams(win) -> SplitterRenderParams
Get the splitter parameters, see wxSplitterRenderParams.
%End
virtual
wxRendererVersion GetVersion() const;
%Docstring
GetVersion() -> RendererVersion
This function is used for version checking: Load() refuses to load any
shared libraries implementing an older or incompatible version.
%End
private:
wxDelegateRendererNative(const wxDelegateRendererNative&);
virtual void DrawTitleBarBitmap(wxWindow* win, wxDC& dc, const wxRect& rect, wxTitleBarButton button, int flags = 0);
%Docstring
DrawTitleBarBitmap(win, dc, rect, button, flags=0)
Draw a title bar button in the given state.
%End
%MethodCode
PyErr_Clear();
Py_BEGIN_ALLOW_THREADS
_wxDelegateRendererNative_DrawTitleBarBitmap(sipCpp, win, dc, rect, button, flags);
Py_END_ALLOW_THREADS
if (PyErr_Occurred()) sipIsErr = 1;
%End
%TypeCode
void _wxDelegateRendererNative_DrawTitleBarBitmap(wxDelegateRendererNative* self, wxWindow* win, wxDC* dc, const wxRect* rect, wxTitleBarButton button, int flags)
{
#ifdef wxHAS_DRAW_TITLE_BAR_BITMAP
self->DrawTitleBarBitmap(win, *dc, *rect, button, flags);
#else
wxPyRaiseNotImplemented();
#endif
}
%End
public:
%Property(name=Version, get=GetVersion)
}; // end of class wxDelegateRendererNative
More information about the PyQt
mailing list