Home | News | Documentation | Download

PyROOT and vector of base and derived classes

Hello,

when one creates vector of base and derived classes like this:


import ROOT

ROOT.gROOT.ProcessLine('.L reproducer.h++g')

elements = ROOT.vector('FunctionBase*')()

elements.push_back(ROOT.FunctionBase())

elements.push_back(ROOT.FunctionDerived1()) # output: <ipython-input-7-850cc0d631fc>:1: RuntimeWarning: creating converter for unknown type "FunctionBase*&&"

elements.push_back(ROOT.FunctionDerived2())

then accessing the base class “FunctionBase” is OK:

elements[0].func1(0) # OK, returns correct result

However, accessing the second element (a derived class “FunctionDerived1”) leads to a crash:

elements[1] # crashing

---------------------------------------------------------------------------
SystemError                               Traceback (most recent call last)
<ipython-input-13-5aa2081a500d> in <module>
----> 1 elements[1]

SystemError: none of the 2 overloaded methods succeeded. Full details:
  FunctionBase*& vector<FunctionBase*>::at(unsigned long __n) =>
    problem in C++; program state has been reset
  FunctionBase*const& vector<FunctionBase*>::at(unsigned long __n) =>
    problem in C++; program state has been reset



===========================================================
There was a crash.
This is the entire stack trace of all threads:
===========================================================
  1. Is there a way how to get on python side the correct class (either base or derived as inserted to the vector “elements” and call its member functions?

  2. I would like to pass this vector of different classes (all derived from a common base class) defined on python side to given c++ class and access the elements back on python side like this:

a = ROOT.Base(elements)

a.elements[0] # ok

a.elements[1] # this, however, hangs (no output)

Any idea how to achieve 1) and 2) ? Is there a way how to get on python side the correct class type? Reproduced on ~2 weeks old master with legacy PyROOT .

Cheers,
Jiri

reproducer.h (1.4 KB)

Hi Jiri,

Interestingly, with the new PyROOT for me it already crashes when accessing elements[0].func1(0). Is there a specific reason you are using the legacy PyROOT?

@eguiraud or @Axel Can you help?

Cheers,
Jakob

Hi,
at first glance I see a couple of problematic things:

  1. in reproducer.h, FunctionDerived1 and FunctionDerived2 should actually inherit from FunctionBase, but they don’t
  2. in elements.push_back(ROOT.FunctionBase()), PyROOT manages the lifetime of the FunctionBase object created there, and since as far as PyROOT can say the only reference to that object goes out of scope already at that line, the underlying C++ object gets destroyed and you end up with a dangling pointer at elements[0] (same for the other elements). You can easily verify this is the case by putting a printout in the class destructor. A Python list would play nicer with Python object lifetimes than a C++ vector

Hope this helps!
Enrico

EDIT:
3. Base::elements is a shared_ptr, so you will have a double delete when Base's dtor destroys elements and PyROOT also destroys elements

@eguiraud Thanks, you are right! Correcting 1, 2 and 3, as you suggested, leads to correct results. I used vector instead of python list as the vector is needed on the c++ side and I don’t know how to pass a python list to a c++ class). Otherwise list is indeed much more convenient on the python side (as it may also hold objects of different types). I’m sending new reproducer reproducer2.h (1.8 KB)

All these commands seems to give correct results now (for both legacy PyROOT and the default one):

import ROOT
ROOT.gROOT.ProcessLine('.L reproducer2.h++g')
elements = ROOT.vector('FunctionBase*')()
e0 = ROOT.FunctionBase()
e1 = ROOT.FunctionDerived1()
e2 = ROOT.FunctionDerived2()
elements.push_back(e0)
elements.push_back(e1)
elements.push_back(e2)
elements
elements[0]
elements[0](0)
elements[0].func1(0)
elements[1]
elements[1](0)
elements[1].func1(0)
a = ROOT.Base(elements)
a.elements[0]
a.elements[0](0)
a.elements[0].func1(0)
a.elements[1]
a.elements[1](0)
a.elements[1].func1(0)

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.