Troubleshooting sip assignHelper != NULL assertion

Scott Talbert swt at techie.net
Tue Jun 2 03:00:57 BST 2020


On Mon, 1 Jun 2020, Phil Thompson wrote:

> On 31/05/2020 17:48, Scott Talbert wrote:
>> 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.
>
> Can you try the current repo.
>
> When you are happy I'll release v4.19.23.

Unfortunately, I'm still running into trouble with this 
wxSplitterRenderParams class.  This is what I get now:

../../../../sip/cpp/sip_corewxRendererNative.cpp: In function ‘PyObject* 
meth_wxRendererNative_GetSplitterParams(PyObject*, PyObject*, PyObject*)’:
../../../../sip/cpp/sip_corewxRendererNative.cpp:1464:52: error: use of 
deleted function ‘wxSplitterRenderParams& 
wxSplitterRenderParams::operator=(wxSplitterRenderParams&&)’
  1464 |             *sipRes = sipCpp->GetSplitterParams(win);
       |                                                    ^
In file included from ../../../../sip/cpp/sip_corewxRendererNative.cpp:12:
../../../../../Phoenix/ext/wxWidgets/include/wx/renderer.h:97:25: note: 
‘wxSplitterRenderParams& 
wxSplitterRenderParams::operator=(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
../../../../sip/cpp/sip_corewxRendererNative.cpp: In function ‘PyObject* 
meth_wxRendererNative_GetVersion(PyObject*, PyObject*)’:
../../../../sip/cpp/sip_corewxRendererNative.cpp:1508:42: error: use of 
deleted function ‘wxRendererVersion& 
wxRendererVersion::operator=(wxRendererVersion&&)’
  1508 |             *sipRes = sipCpp->GetVersion();
       |                                          ^
In file included from ../../../../sip/cpp/sip_corewxRendererNative.cpp:12:
../../../../../Phoenix/ext/wxWidgets/include/wx/renderer.h:141:25: note: 
‘wxRendererVersion& wxRendererVersion::operator=(wxRendererVersion&&)’ is 
implicitly deleted because the default definition would be ill-formed:
   141 | struct WXDLLIMPEXP_CORE wxRendererVersion
       |                         ^~~~~~~~~~~~~~~~~
../../../../../Phoenix/ext/wxWidgets/include/wx/renderer.h:141:25: error: 
non-static const member ‘const int wxRendererVersion::version’, cannot use 
default assignment operator
../../../../../Phoenix/ext/wxWidgets/include/wx/renderer.h:141:25: error: 
non-static const member ‘const int wxRendererVersion::age’, cannot use 
default assignment operator


I looked at what SIP 4.19.19 generated for sip_corewxRendererNative.cpp, 
and this is what it did:

sipRes = new ::wxSplitterRenderParams(sipCpp->GetSplitterParams(win));

So, it seems the wxSplitterRenderParams class does actually have an 
implicit copy constructor that works.  So, it doesn't have an assignment 
operator but DOES have a copy constructor.

Thanks,
Scott


More information about the PyQt mailing list