I’m working with a python-based framework which defines custom C structures to be saved using RDataFrame snapshots. The structures are defined during execution by calling ROOT.gInterpreter.Declare(), instead of having them defined in dedicated files. When saving the snapshots to a .root file, the following error arises:
Error in TTree::Branch: The class requested (vector<FatJetStruct>) for the branch “FatJets” is an instance of an stl collection and does not have a compiled CollectionProxy. Please generate the dictionary for this collection (vector) to avoid to write corrupted data.
I tried creating the corresponding dictionary by adding a line ROOT.gInterpreter.GenerateDictionary("vector<FatJetStruct>","vector") before snapshoting the RDataFrame. Four files are created (.cxx, .d, .pcm, .so), however, the error does not go away. Is there a way to validate that the generated dictionary? Is there a step missing in which the dictionary needs to be loaded after creation?
.IsLoaded() returns False. I’m not sure if the generated dictionary is incorrect because of the problem described below, causing it not to load. However, no warnings or errors are printed during dictionary generation.
The problem is most likely in the non-existing header file. The framework I’m using defines the structure at execution time and uses gInterpreter to declare it. This is probably done this way because the content of the desired structure may change depending on the type of rootfile used (more specifically, NanoAOD version).
Is there a way to generate and load a dictionary for a class which only exists in memory (i.e., there is no corresponding header file)?
If not, a routine could be added to the framework which would write a header file and generate a dictionary if it does not exist. The linked tutorials should be instructive enough to help me with that.
Hi Matej,
I see, so you jit the definition of the class and then want to perform I/O of that type. I would expect this to be possible in principle, but I could not make it work either. I tried e.g.
#include <ROOT/RDataFrame.hxx>
#include <TInterpreter.h>
int main() {
gInterpreter->Declare(R"(#pragma link C++ class MyClass;
#pragma link C++ class vector<MyClass>;
struct MyClass { int data = -1; };)");
{
ROOT::RDataFrame(10)
.Define("c",
"std::vector<MyClass>{{int(rdfentry_)}, {int(rdfentry_)}};")
.Snapshot("t", "f.root", {"c"});
TFile f("f.root");
auto *t = f.Get<TTree>("t");
t->Scan();
}
}
Maybe @pcanal or @Axel can suggest the right spell (or otherwise they could confirm that this is not possible and you need a separate header that you can pass to GenerateDictionary).