pythoncppyy

cppyy template class instantiation has no virtual destructor


I have the following child class set up in my Python code:

class NodeRewriter(SyntaxRewriter[SyntaxNode]):
   
   def visit(self, node: SyntaxNode):
      print(node)

Here are the relevant objects:

RuntimeWarning: class "slang::SyntaxRewriter<slang::SyntaxNode>" has no virtual destructor
  class NodeRewriter(SyntaxRewriter[SyntaxNode]):
>>> print(SyntaxRewriter)
<cppyy.Template 'slang::SyntaxRewriter' object at 0x7fb76c0c15b0>
>>> print(SyntaxNode)
<class cppyy.gbl.slang.SyntaxNode at 0x55e4302f07c0>
>>> print(SyntaxRewriter[SyntaxNode])
<class cppyy.gbl.slang.SyntaxRewriter<slang::SyntaxNode> at 0x55e4308df4b0>
>>> 

All I am doing is creating the class, I haven't created any objects from it yet. I get the warning when I import the file containing the class and the rest of the cppyy code.

Do I need to add a __destruct__() method myself? It seems the warning is not referring to my child class but to the template instantiation it inherits from.


Solution

  • Without having seen the source of SyntaxRewriter ...

    If a class in C++ does not have a virtual destructor, it's typically not suitable as a base class, because if delete is called on a base class pointer, only the destructor of the base is called, not the one of the actual derived class underlying the pointer. Sometimes that's okay, sometimes you get resource leaks, sometimes you get spurious crashes.

    If all done in C++, it's more explicit when you're doing this and it's fine as long as you never delete such a base class pointer. The code being explicit, you can take care to never do this. But in cross-derivation, it may not be obvious that there is a stub in between the C++ base class and the Python derived class, both to mediate calls and manage memory. Both that stub and the actual deletion are thus hidden. Furthermore, the most common use case for cross-derivation is to send instances of the derived Python class into C++ land, where they are likely to be managed. Then, if C++ at some point cleans up the object, without a virtual destructor of the C++ base, the destructor of the stub will not be called, and the Python portion will leak (it will have a reference count too many that never gets cleared). It's only a warning b/c if Python manages the object, there won't be a leak even if there's no virtual destructor.

    Implementing a __destruct__ won't make a difference (and shouldn't be done in any case; use __del__ instead if resource cleanup is needed), because the problem, if any, is caused by previously compiled C++ and can't be changed after the fact.

    (Note that Python warnings can be suppressed, see https://docs.python.org/3/library/warnings.html .)