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