TTreeReader over classes defined only by header file

This is related to my work reading TTree data into Avro or the JVM. Currently, I require users to have a dynamic library with TDictionaries, e.g. Event_cxx.so and Event_cxx_ACLiC_dict_rdict.pcm. I’d like to relax that requirement because all of the class structure information is already in the ROOT file in the form of TStreamers (ignoring complications like CMSSW’s custom streamers).

I know how to generate a header file from the TStreamer info, and anyway if I required users to supply a C++ header file, at least a text file is a lot more platform-independent than dynamic libraries. Furthermore, I know that a header file with all its method definitions (or no methods) can be compiled by Cling with

.L fileName.h
or

gInterpreter->Declare(fileAsString);

both of which can be accessed programmatically. This loads the class to the degree that it can be referenced with

TClass::GetClass(className);

and while it doesn’t have a

TClass::GetClass(className)->GetTypeInfo()

that TTreeReaderValue requires to get a TDictionary, I can get the TDictionary with

TDictionary::GetDictionary(className)

and use that in a custom subclass of TTreeReaderValueBase. However, when ROOT actually tries to read data into that class, it encounters a “TClass::New: cannot create object of class” error. Here’s a full interactive session illustrating what I’m trying to do.

[code]root [0] .L test_Event/Event.h
root [1] TFile *f = TFile::Open(“test_Event/Event.root”)
(TFile ) 0x1c56530
root [2] TTreeReader r(“T”, f)
(TTreeReader &) Name: TTreeReader Title: A simple interface to read trees
root [3] TDictionary::GetDictionary(“Event”)
(TDictionary ) 0x1f6a440
root [4] class MyReader: public ROOT::Internal::TTreeReaderValueBase {
root (cont’ed, cancel with .@) [5]public:
root (cont’ed, cancel with .@) [6] MyReader(TTreeReader &r, const char
branchname, const char
classname) : TTreeReaderValueBase(&r, branchname, TDictionary::GetDictionary(classname)) { }
root (cont’ed, cancel with .@) [7] const char *GetDerivedTypeName() const { return “blah”; }
root (cont’ed, cancel with .@) [8]};
root [9] MyReader myreader(r, “event”, “Event”)
(MyReader &) @0x7f2c4cdd5108
root [10] r.Next()
(Bool_t) true
root [11] myreader.GetAddress()
Error in TClass::New: cannot create object of class Event

*** Break *** segmentation violation
Generating stack trace…
0x00007f2c3f874380 in ROOT::Detail::TBranchProxy::Setup() + 0xc90 from /opt/root_v6.06.00/lib/libTreePlayer.so
0x00007f2c3f84f0e7 in ROOT::Internal::TTreeReaderValueBase::ProxyRead() + 0x67 from /opt/root_v6.06.00/lib/libTreePlayer.so
0x00007f2c3f84f929 in ROOT::Internal::TTreeReaderValueBase::GetAddress() + 0x9 from /opt/root_v6.06.00/lib/libTreePlayer.so
0x00007f2c4cd9d035 in
0x00007f2c48de8b13 in cling::Interpreter::RunFunction(clang::FunctionDecl const*, cling::Value*) + 0x1b3 from /opt/root_v6.06.00/lib/libCling.so
0x00007f2c48dee217 in cling::Interpreter::EvaluateInternal(std::basic_string<char, std::char_traits, std::allocator > const&, cling::CompilationOptions, cling::Value*, cling::Transaction**) + 0x157 from /opt/root_v6.06.00/lib/libCling.so
0x00007f2c48dee353 in cling::Interpreter::process(std::basic_string<char, std::char_traits, std::allocator > const&, cling::Value*, cling::Transaction**) + 0x83 from /opt/root_v6.06.00/lib/libCling.so
0x00007f2c48e85c93 in cling::MetaProcessor::process(char const*, cling::Interpreter::CompilationResult&, cling::Value*) + 0x233 from /opt/root_v6.06.00/lib/libCling.so
0x00007f2c48d73736 in TCling::ProcessLine(char const*, TInterpreter::EErrorCode*) + 0x906 from /opt/root_v6.06.00/lib/libCling.so
0x00007f2c4c708eb0 in TApplication::ProcessLine(char const*, bool, int*) + 0x3b0 from /opt/root_v6.06.00/lib/libCore.so
0x00007f2c4cb5c1bf in TRint::ProcessLineNr(char const*, char const*, int*) + 0x9f from /opt/root_v6.06.00/lib/libRint.so
0x00007f2c4cb5c4d1 in TRint::HandleTermInput() + 0x221 from /opt/root_v6.06.00/lib/libRint.so
0x00007f2c4c7f17c5 in TUnixSystem::CheckDescriptors() + 0x155 from /opt/root_v6.06.00/lib/libCore.so
0x00007f2c4c7f275a in TUnixSystem::DispatchOneEvent(bool) + 0xca from /opt/root_v6.06.00/lib/libCore.so
0x00007f2c4c73b006 in TSystem::InnerLoop() + 0x16 from /opt/root_v6.06.00/lib/libCore.so
0x00007f2c4c73bc10 in TSystem::Run() + 0x70 from /opt/root_v6.06.00/lib/libCore.so
0x00007f2c4c70731f in TApplication::Run(bool) + 0x1f from /opt/root_v6.06.00/lib/libCore.so
0x00007f2c4cb5da38 in TRint::Run(bool) + 0x538 from /opt/root_v6.06.00/lib/libRint.so
0x00000000004010ac in main + 0x4c from /opt/root_v6.06.00/bin/root.exe
0x00007f2c4bc76ec5 in __libc_start_main + 0xf5 from /lib/x86_64-linux-gnu/libc.so.6
0x000000000040111a in from /opt/root_v6.06.00/bin/root.exe
[/code]

Any ideas? Is there a way to do this differently? Or is this a bug? (I’ve strayed far from the path of what a normal user might do, so I’m not sure if this is supposed to be undefined behavior or not.)

Thanks!

Hi Jim,

Two separate issues.

One is the crash in ROOT::Detail::TBranchProxy::Setup; this is a bug, it should actually fail elegantly :slight_smile:.

The second is the fact that TClass::New fail … and this one is because the interpreter/compiler does not have enough information. Namely the implementation of the Event constructor is missing. For example switching from loading Event.h to loading Event.cxx should fix the problem.

Cheers,
Philippe.

Hi Jim,

I am unable to reproduce the crash; with the master I get:[code]root [7] myreader.GetAddress()
Error in TClass::New: cannot create object of class Event
Error in TBranchProxy::Read: Unable to initialize event

(void *) nullptr
root [8]
[/code]Which version of ROOT are you using?

Cheers,
Philippe

I’m using 6.06/00 (precompiled for Ubuntu from the downloads page).

I tested it (with my version of ROOT) and you’re right: loading Event.cxx is sufficient to read Event.root. (I mistakenly thought that the header file contained the complete definition of these classes; I must have missed the stub methods.)

Since you can’t reproduce the crash on the master branch, it looks like the bug was fixed.

But this is great! I can now push the equivalent of this interactive session through my code and at least only require the source code for a user’s class. After that, I’ll add MakeProject-like generation from the TStreamer data (using your MakeProxy example) so that the ROOT file alone is sufficient.

Thanks!