Troubleshooting sip assignHelper != NULL assertion

Phil Thompson phil at riverbankcomputing.com
Sun May 31 17:04:10 BST 2020


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?

Phil


More information about the PyQt mailing list