1

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.

273K
  • 29,503
  • 10
  • 41
  • 64
DLAN
  • 581
  • 1
  • 4
  • 10

1 Answers1

1

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

Wim Lavrijsen
  • 3,453
  • 1
  • 9
  • 21
  • Okay, that makes sense, thanks. After working with the library a bit more, I have discovered that the type of the template should be the derived class itself. Correct me if I am wrong, but I don't think this is doable since Python won't be able to reference the class before it is created. Basically this: `class NodeRewriter(SyntaxRewriter[NodeRewriter]):`. I will probably end up creating the derived class in C++ then subclassing it on the Python side in order to skip the template. – DLAN Sep 18 '21 at 17:14
  • 1
    Yes, that particular syntax will never be valid Python, but it's not logically impossible. Point being that the C++ stub is what would be used in the template, which is a base class of the Python class, and hence exists before the Python class itself. I.e. it may be possible by creating some specialized syntax that doesn't mention the class name as an identifier (the stub name is not known on the Python side, so as to not pollute the hierarchy, thus can't be used directly). – Wim Lavrijsen Sep 19 '21 at 04:43
  • Okay good to know. I will figure out another solution, either creating the derived class on the C++ side, or just bypass it entirely and implement the functionality myself in a Python class of my own. – DLAN Sep 19 '21 at 04:53