Troubleshooting sip assignHelper != NULL assertion

Phil Thompson phil at riverbankcomputing.com
Tue Jun 2 05:02:58 BST 2020


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...

Phil


More information about the PyQt mailing list