Troubleshooting sip assignHelper != NULL assertion

Phil Thompson phil at riverbankcomputing.com
Tue Jun 2 14:28:55 BST 2020


On 02/06/2020 14:03, Scott Talbert wrote:
> On Tue, 2 Jun 2020, Phil Thompson wrote:
> 
>> On 02/06/2020 03:00, Scott Talbert wrote:
>>> 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.
>> 
>> So if you take out the declaration of the private copy ctor from the 
>> .sip file...
> 
> I did try that way as well, but in that case I get:
> 
> ../../../../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

...so follow the same logic as the copy ctor and define a private 
assignment operator.

Phil


More information about the PyQt mailing list