my program uses ROOT to save intermediate analysis results to root files using ExRootAnalysis.
The classes I am saving are mainly a collection of member variables of type float and some functions filling these members. I am using the ClassDef macro and avoid code duplication by inheriting common members from base classes. I do not define any streamers.
Recently I have switched to ROOT6.
I am generating the dictionary as before, additionally I have moved the new *_rdict.pcm files in my library folder. But when I run my program I get the error message
and the resulting files have a more complicated structure resembling the top most inheritance step.
Which of course prevents later analysis to access the branches via TTree::SetBranchStatus and TTree::SetBranchAddress
it is not possible to split instances of classes with a custom streamer. The streamer method implemented by the user is “a black box” for ROOT which is called to (de)serialse the objects performing user defined actions: the single data members cannot be stored in different “columns”.
What Valerian describes in the post you cite is related but it does not have a big overlap with your issue.
I am afraid, that i do not really understand what you are trying to explain to me.
What do you mean be splitting instances of classes? I have only an inheritance hierarchy, which did not show up in ROOT5 files, and I want it to stay like this also with ROOT6 files. Additionally, I have not implemented a streamer method other than what the ClassDef macro is implementing.
so the problem is that the class which root says it has a custom streamer has no custom streamer: is this the problem?
If yes, could you post a reproducer for this issue?
here is a minimal program (main.cpp) which saves triples of random numbers to a class Three (Numbers.hh) inherited from the class Two (Numbers.hh).
In my setup I use CMake to build the files and generate the necessary dictionaries from Numbers.cpp using TestLinkDef.hh.
The output test.root contains the triples of random numbers but they are split up according the the class inheritance (Three<-Two).
With Root5 this gives flat files, containing just the triples.
looking at your linkdef, you are missing trailing “+” after the selection pragmas, which allow rootcling to generate automatic streamers for your classes. The usage of “+” is reccomended for all new classes (see rootcling -h for all the details). I attach a standalone, i.e. no atlas class needed, version of your example for future reference.
Cheers,
Danilo
LinkDef.h
#ifdef __MAKECINT__
#pragma link C++ class Two+;
#pragma link C++ class Three+;
#pragma link C++ class vector<Two>+;
#pragma link C++ class vector<Three>+;
#endif
main.cpp
#include <vector>
#include "TFile.h"
#include "TTree.h"
#include "TRandom.h"
#include "Numbers.hh"
int main()
{
TFile file("test.root", "recreate");
TTree tree("tree","tree");
Two two;
tree.Branch("two", &two);
std::vector<Two> twos;
tree.Branch("twos", &twos);
std::vector<Three> threes;
tree.Branch("threes", &threes);
TRandom random;
for (int i = 0; i < 10; ++i) {
threes.push_back(Three(random.Rndm(), random.Rndm(), random.Rndm()));
twos.push_back(Two(random.Rndm(), random.Rndm()));
two = twos[0];
tree.Fill();
}
tree.Write();
file.Close();
}
Numbers.hh
#ifndef __CLASSES__
#define __CLASSES__
#include "TObject.h"
class Two: public TObject
{
private:
float X;
float Y;
public:
Two();
Two(float x, float y):X(x),Y(y){};
~Two();
void SetX(float x);
void SetY(float y);
void Set(float x, float y);
ClassDef(Two, 10)
};
class Three : public Two
{
private:
float Z;
public:
Three();
Three(float x, float y , float z);
void SetZ(float z);
void Set(float x, float y , float z);
ClassDef(Three, 1)
};
Two::Two()
{
X = 0;
Y = 0;
}
Two::~Two() {}
void Two::SetX(float x)
{
X = x;
}
void Two::SetY(float y)
{
Y = y;
}
void Two::Set(float x, float y)
{
SetX(x);
SetY(y);
}
Three::Three()
{
Z = 0;
}
Three::Three(float x, float y, float z)
{
Set(x, y, z);
}
void Three::SetZ(float z)
{
Z = z;
}
void Three::Set(float x, float y, float z)
{
Two::Set(x, y);
SetZ(z);
}
#endif
That was embarrassingly simple. This is probably the most common LinkDef mistake.
I must admit that I introduced this problem after switching to ROOT6 because my old LinkDef stopped working.
I define everything that needs a dictionary in one file lets say “Numbers.hh” and I used to have a really simple LinkDef which I never had to update
#ifdef __MAKECINT__
#pragma link C++ defined_in "Numbers.hh";
#endif
But somehow this doesn’t work anymore. I would prefer if I could keep this simple file as it reduces the possibilities for errors, such as forgetting the ‘+’.
Do you have any idea what has changed with the “defined in” pragma?
you are right, generating dictionaries by hand from the folder where the LinkDef is located works well.
But when I generate the dictionary from a different folder I have to give the full path to the Numbers.hh in the LinkDef.hh. I didn’t have to do that before.
I do generate the dictionary from a different folder because I am using CMake which processes all the source files from a separate build folder.
I am passing the include folders via “-I” to rootcint/rootcling but apparently it stopped picking up the include paths.
I will look into this.
For the moment, is it possible to modify the rootcling invocation in your cmake moving the directory for inclusion to the absolute path to the header?
yes right now I call the header file with the long relative path from the build directory to the include directory. This works, but is more error prone than before.
For me this is not an urgent problem any more.