Convertying pyroot TFile in memory to c++ TFile

Hi everyone,

This may be some wishful thinking, but I’m wondering if there is a way to convert at pyroot TFile in memory to a c++ TFile in memory so I can pass it from python to a c++ function I have wrapped. I’ve made a simple wrapper of of a c++ class using the popular ctypes hack. I have a cpp file sort of like the one below.
Foo.cc:

class Foo{
    public:
        void bar(TFile* f){
           // some stuff with f
        }
};
extern "C" {
    Foo* Foo_new(){ return new Foo(); }
    void Foo_bar(Foo* foo, TFile* f){ foo->bar(f); }
}
}

Which I can compile into a shared library with:

g++ -o Foo.so -shared -fPIC Foo.cc

It would be awesome if I could call this from python with something like:

from ctypes import cdll
import ROOT as rt

lib = cdll.LoadLibrary('./Foo.so')

class Foo(object):
    def __init__(self):
        self.obj = lib.Foo_new()

    def bar(self, f):
        lib.Foo_bar(self.obj, f)

f = rt.TFile("rando_file.root", "NEW")
f_c = f.pyrootTFileToCppTFile() // is there anything like this??
foo = Foo()
foo.bar(f_c)

Is it possible to “unwrap” a pyroot TFile in this way or am I just another foolish newbie?

Thanks!


Please read tips for efficient and successful posting and posting code

ROOT Version: 5.34/07
Platform: Ubuntu 16.04.6
_Compiler: g++ 49.3-13ubuntu2


I’m not sure I understand your question, but you can pass python objects to C++ code in a python session using TPython::Eval:

In [1]: import ROOT

In [2]: f = ROOT.TFile.Open('temp.root', 'recreate')

In [3]: ROOT.gInterpreter.ProcessLine(
   ...:     'TFile* fcpp = TPython::Eval("f");'
   ...:     'fcpp->ls();'
   ...: )
TFile**		temp.root	
 TFile*		temp.root	
Out[3]: 0L

Though I couldn’t swear this was available way back in v5.34/07…

(Note that TPython can only access objects in globals, not locals.)

It would be awesome if I could call this from python with something like:

Yes! This is the main strength of PyROOT (i.e. make this bridge transparent). All you need to do is make PyROOT (via Cling) aware of your class. One way is to generate a ROOT dictionary and bundle it with your class another is to load the header file explicitly

lib = cdll.LoadLibrary('./Foo.so')
ROOT.gInterpreter.declare("#include \"Foo.h\"") # Needed only if Foo.so does not contain a dictionary

Hi @pcanal,
Thank you for the response, and sorry for the late reply. Shamefully, the version of root I am using right now uses CINT instead of cling… I will look into installing cling as this looks like by far the best solution.

In the meantime, I don’t totally understand what it means to generate a ROOT dictionary and bundle it with my class. Does this method work with regular old CINT? I’ve read through this page, but am unclear about what I would need to do make a ROOT dictionary and then bundle it with another class. If you know of an online resource that details this approach, I would be greatly appreciative!

Thanks,
jss

Does this method work with regular old CINT?

Yes.

but am unclear about what I would need to do make a ROOT dictionary and then bundle it with another class.

You need to write a file with a name suffixed by LinkDef.h. So for example FooLinkDef.h that contains request for dictionary for your classes:

#ifdef __MAKECINT__
#pragma link C++ class SomeClass+;
#pragma link C++ class SomeOtherClass+;
#endif

You then run rootcling (or rootcint) to generate a source file (nicknamed ‘dictionary’):

rootcling -f foodict.cxx -c Foo.h FooLinkDef.h

You then need to compile foodict.cxx and include the resulting object file into your Foo.so library (i.e. add it to the link line).

Cheers,
Philippe.

Exactly what I needed, thank you!

Hi @pcanal,

Thank you again for those detailed instructions. I have gone through this process, but still cannot seem to pass the pyroot TFile through ctypes cdll to my C++ function. Here is what I’ve done:

Foo.h:

# include <iostream>
# include <TFile.h>

class Foo{
    public:
        void bar(TFile* f){
        	f->ls();
        	std::cout << "blah" << std::endl;
        }
};

extern "C" {
    Foo* Foo_new(){ return new Foo(); }
    void Foo_bar(Foo* foo, TFile* f){ foo->bar(f); }
}

PyFoo.py:

from ctypes import *
import ROOT as rt

lib = cdll.LoadLibrary('./Foo.so')

class Foo(object):
    def __init__(self):
        self.obj = lib.Foo_new()
    def bar(self, f):
        lib.Foo_bar(self.obj, f)

f = rt.TFile("rando_file.root", "RECREATE")
# Ive also tried 
#f = pointer(rt.TFile("rando_file.root", "RECREATE")) 
# and 
#f = byref(rt.TFile("rando_file.root", "RECREATE"))
foo = Foo()
foo.bar(f)

FooLinkDef.h:

#ifdef __MAKECINT__
#pragma link C++ class Foo+;
#endif

running rootcint:

rootcint -f foodict.cxx -c Foo.h FooLinkDef.h

compiling foodict.cxx to library:

g++ -I$ROOTSYS/include -shared -fpic foodict.cxx -o ./libfoodict.so

compiling Foo.h to shared library and linking to libfoodict.so

g++ -L<path/to/libfoodict.so> -I$ROOTSYS/include -shared Foo.h -o ./libfoo.so -lfoodict

This all worked without a hitch, but when I got to run PyFoo.py I get the error:

Traceback (most recent call last):
  File "PyFoo.py", line 22, in <module>
    foo.bar(f)
  File "PyFoo.py", line 12, in bar
    lib.Foo_bar(self.obj, f)
ctypes.ArgumentError: argument 2: <type 'exceptions.TypeError'>: Don't know how to convert parameter 2

Have I just run into a limitation of ctypes? How do people normally wrap c++ functions that take root objects as parameters? Thanks again for all the help. I hope other newbies might find this post useful in the future.

-jss

Note that the Foo class is already provided for you in python by PyROOT:
i.e. this should work:

from ctypes import *
import ROOT as rt

lib = cdll.LoadLibrary('./Foo.so')

f = rt.TFile("rando_file.root", "RECREATE")
foo = rt.Foo()
foo.bar(f)

Hmmm, I get the following error when I try that:

Traceback (most recent call last):
  File "PyFoo.py", line 12, in <module>
    foo = rt.Foo()
  File "/data/snoplus/shared/code/ratcage/root-5.34.07/lib/ROOT.py", line 457, in __getattr2
    attr = _root.LookupRootEntity( name )
AttributeError: Foo

Switching to Foo() to Foo_new() to match what is in extern gives the same thing. Any ideas @pcanal ?

Try with

g++ -I$ROOTSYS/include -c -fpic foodict.cxx -o ./foodict.o
g++ -I$ROOTSYS/include -c -fpic Foo.h -o ./Foo.so
g++  -shared -fpic Foo.o foodict.o -o ./libfoo.so

Since Foo.h does not use any symbol from libfoodict.so it might not be using it.

Also after loading the library you can try

rt.TClass.GetClass('Foo')

which should return a non null value if the library is ‘well formed’.

Thank you! This worked with the exception that I had to change Foo.h to Foo.cc, I think the compiler does some funky things when it tries to compile header files. Future implementations could of course create a Foo.h file and then include it in a Foo.cc file which they would then compile. For posterity sake ill also mention that Foo.so should be changed to Foo.o in the above comment (just a little typo). For future viewers, the fully functioning version looks like this:

Foo.cc:

# include <iostream>
# include <TFile.h>

class Foo{
    public:
        void bar(TFile* f){
        	f->ls();
        	std::cout << "blah" << std::endl;
        }
};

FooLinkDef.cc:

#ifdef __MAKECINT__
#pragma link C++ class Foo+;
#endif

running rootcint and compiling:

$ rootcint -f foodict.cxx -c Foo.cc FooLinkDef.h
$ g++ -I$ROOTSYS/include -c -fpic foodict.cxx -o ./foodict.o
$ g++ -I$ROOTSYS/include -c -fpic Foo.cc -o ./Foo.o
$ g++  -shared -fpic Foo.o foodict.o -o ./libfoo.so

Calling the dang thing in python:

from ctypes import *
import ROOT as rt

lib = cdll.LoadLibrary('./libfoo.so')

f = rt.TFile("rando_file.root", "RECREATE")
foo = rt.Foo()
foo.bar(f)