Troubleshooting sip assignHelper != NULL assertion
Phil Thompson
phil at riverbankcomputing.com
Sun May 31 09:00:48 BST 2020
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.
Phil
More information about the PyQt
mailing list