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