Question about how to cast in pyROOT

I have an abstract class TClassA which is a base class for TClassB.

Now I have an instance of TClassB and a method MethodX that receives as argument TClassA *a.

In C++ this would be done as:

TClassB *b = new TClassB();
MethodX( (TClassA *) b );

In practice, inside my code I got the following error:

>>> rawEv = ROOT.TRestRawSignalEvent
>>> rn.SetInputEvent(rawEv)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: void TRestRun::SetInputEvent(TRestEvent* event) =>
    TypeError: could not convert argument 1

where TRestRawSignalEvent inherits from TRestEvent.

How can I succeed to write that code in pyROOT.

Maybe @Axel or @vpadulan can help

May be

Hi @Javier_Galan ,
Sorry for the latency. Maybe I don’t understand your question completely, but doesn’t something like this work for you?

import ROOT

ROOT.gInterpreter.Declare("""
    struct A {};
    struct B: public A {
        int methodB(A *a) { return 42; }
    };
""")

b = ROOT.B()
a = ROOT.A()
print(b.methodB(a))

I cannot create an instance of A because it is abstract, I believe that is the main difference with your example.

Perhaps it would be more like:

import ROOT

ROOT.gInterpreter.Declare("""
    struct C {
         int methodC( A *a ) { return 42; }
    };
    struct A {};
    struct B: public A {
    };
""")

c = ROOT.C()
b = ROOT.B()
print(c.methodC(b))

A is abstract and I cannot create an instance.

Also this would work, given A an abstract class. Can you apply this to your case perhaps?

import ROOT

ROOT.gInterpreter.Declare("""
    struct A { virtual void abstractmethod() = 0; };
    struct B: public A {
        void abstractmethod() final {}
        int methodB(A *a) { return 42; }
    };
    struct C: public A{
        void abstractmethod() final {}
    };
""")


b = ROOT.B()

c = ROOT.C()

print(b.methodB(c))

That example works.

Our case would be more as follows:

import ROOT

ROOT.gInterpreter.Declare("""
    struct A { virtual void abstractmethod() = 0; };
    struct B: public A {
        void abstractmethod() final {}
    };
    struct C{
        int methodC(A *a) { return 42; }
    };
""")


b = ROOT.B()

c = ROOT.C()

print(c.methodC(b))

which also works.

In our case we have the following classes which inherit from TObject.

A= TRestEvent. It contains only one abstract method void Initialize() = 0
B = TRestRawSignalEvent
C = TRestRun

abstractmethod = TRestEvent::Initialize()
methodC = TRestRun::SetInputEvent (TRestEvent *ev).

It is compiled code, not interpreted. Not sure if that would make a difference.

Thanks!!

But if I change the struct keyword by class, I get an error

>>> import ROOT
>>> 
>>> ROOT.gInterpreter.Declare("""
...     class A { virtual void abstractmethod() = 0; };
...     class B: public A {
...         void abstractmethod() final {}
...     };
...     class C{
...         int methodC(A *a) { return 42; }
...     };
... """)
True
>>> 
>>> 
>>> b = ROOT.B()
>>> 
>>> c = ROOT.C()
>>> 
>>> print(c.methodC(b))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute 'methodC'

Ok, glad the examples work! For your last case, the error is due to the fact that changing to class makes the methodC private in that class. You need to make it public.
Cheers,
Vincenzo

Ok that solves the problem with the example, however this does not solve our problem with our compiled class, which is the main topic for this post.

There must be something I don’t understand. Why do you expect to be able to use a private method of a class from outside that class?

Hi @Javier_Galan,

I find it weird that it works for code injected into the interpreter, but not for compiled classes. Could you please attach a minimal reproducer so that we can better diagnose (or debug) the issue?

As a (hacky) workaround, maybe you could define an inline member function on the C++ side that returns a pointer to the base object, as in

class TRestRawSignalEvent : public TRestEvent {
public:
   // ...
   TRestEvent *AsTRestEvent() { return static_cast<TRestEvent *>(this); }
};

and, obviously, on the Python side

rawEv = ROOT.TRestRawSignalEvent
rn.SetInputEvent(rawEv.AsTRestEvent())

In any case, looking forward to your reproducer.

Cheers,
J.

I can provide the following

git clone git@github.com:rest-for-physics/framework.git
cd framework
python3 pull-submodules.py --onlylibs
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=../install -DRESTLIB_RAW=ON ..
make -j install
source ../install/thisREST.sh

Then I added the method you suggested into TRestRawSignalEvent and tested in python3

>>> import ROOT
>>> import REST
>>> rn = ROOT.TRestRun()
>>> rawEv = ROOT.TRestRawSignalEvent()
>>> rn.SetInputEvent( rawEv.AsTRestEvent() )

and the error output

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: TRestEvent* TRestRawSignalEvent::AsTRestEvent() =>
    TypeError: unbound method TRestRawSignalEvent::AsTRestEvent must be called with a TRestRawSignalEvent instance as first argument

This snippet should be equivalent to what you are doing (based on your previous examples, corrected class names and also adding the latest AsTRestEvent method). But all the classes feature their methods in the public section in your own repository, so I still don’t understand what was your previous issue with using the private methods.

import ROOT

ROOT.gInterpreter.Declare("""
    class TRestEvent {
    public:
        virtual void Initialize() = 0;
    };
    class TRestRawSignalEvent: public TRestEvent {
    public:
        void Initialize() final {}
        TRestEvent *AsTRestEvent() { return static_cast<TRestEvent *>(this); }
    };
    class TRestRun{
    public:
        void SetInputEvent(TRestEvent *) {}
    };
""")

rn = ROOT.TRestRun()
rawEv = ROOT.TRestRawSignalEvent()
rn.SetInputEvent(rawEv)
rn.SetInputEvent(rawEv.AsTRestEvent())

This fails as it is, what are the requirements to build the project? I’ve hit a couple issues, first I didn’t have a ROOT installation before running the cmake command and it complained. Then I sourced ROOT and now it complains that it can’t find TEveArrow.h.

It requires ROOT 6.26/10, then, as your ROOT seems to be not compiled with Eve libraries, just disable it.

Add the following option

cmake -DREST_EVE=OFF ..

I cannot build on ArchLinux due to notable changes in the GNU mpfr headers (using version 4.1.1). Could you provide us additional information about your build environment?

@jalopezg Try (if it fails, use “-DREST_MPFR=OFF”):

REST_MPFR=OFF should be the default option.

So just compiling with:

cmake -DCMAKE_INSTALL_PREFIX=../install -DRESTLIB_RAW=ON -DREST_EVE=OFF ..

should do the job.

Both of the following commands lead to the same error when building

cmake -DREST_MPFR=OFF -DCMAKE_INSTALL_PREFIX=../install -DRESTLIB_RAW=ON -DREST_EVE=OFF ..
cmake -DCMAKE_INSTALL_PREFIX=../install -DRESTLIB_RAW=ON -DREST_EVE=OFF ..

Error:

make -j install
[...]
[ 17%] generating: /home/vpadulan/Programs/framework/build/rootdict/CINT_TRestTask.cxx with /home/vpadulan/Programs/framework/source/framework/core/inc/TRestTask.h;/home/vpadulan/Programs/framework/build/rootdict/TRestTask_Linkdef.h
In file included from input_line_9:3:
In file included from /home/vpadulan/Programs/framework/source/framework/tools/inc/TRestComplex.h:27:
/home/vpadulan/Programs/framework/source/framework/tools/inc/mpreal.h:121:10: fatal error: 'mpfr.h' file not found
#include <mpfr.h>
         ^~~~~~~~