Troubleshooting sip assignHelper != NULL assertion

Scott Talbert swt at techie.net
Tue Jun 2 14:03:02 BST 2020


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

Scott


More information about the PyQt mailing list