Troubleshooting sip assignHelper != NULL assertion

Scott Talbert swt at techie.net
Sun May 31 16:29:05 BST 2020


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?

Scott


More information about the PyQt mailing list