Problem is following. I have a class, lets call it TestClass with TClonesArray holding other class.
class TestClass : public TObject
{
TClonesArray * data;
};
The other class is GoodTrackSim defined as follow
class BaseTrack : public TLorentzVector {};
class BaseTrackSim : public virtual BaseTrack {};
class GoodTrack: public virtual BaseTrack {};
class GoodTrackSim: public GoodTrack, public BaseTrackSim {};
Of course, each of classes above has ClassDef
and ClassImp
macros.
When creating the branch, the program gives following warining and seg faults with following stack track
error<TClonesArray::SetClass>: GoodTrackSim must inherit from TObject as the left most base class.
… some lines
error<TUnixSystem::DispatchSignals>: segmentation violation
… some lines here
#5 0x00007f57e677e7d4 in TClass::CallShowMembers(void*, TMemberInspector&, int) const () from $ROOTSYS/lib/libCore.so.5.34
#6 0x00007f57e678b2dd in TClass::BuildRealData(void*, bool) () from $ROOTSYS/lib/libCore.so.5.34
#7 0x00007f57e3e14d63 in TTree::BuildStreamerInfo(TClass*, void*, bool) () from $ROOTSYS/lib/libTree.so.5.34
#8 0x00007f57e3e5bd01 in TBranchElement::Unroll(char const*, TClass*, TClass*, char*, int, int, int) () from $ROOTSYS/lib/libTree.so.5.34
#9 0x00007f57e3e5e27a in TBranchElement::Init(TTree*, TBranch*, char const*, TStreamerInfo*, int, char*, int, int, int) () from $ROOTSYS/libTree.so.5.34
#10 0x00007f57e3e5eba2 in TBranchElement::TBranchElement(TBranch*, char const*, TStreamerInfo*, int, char*, int, int, int) () from $ROOTSYS/libTree.so.5.34
#11 0x00007f57e3e18e37 in TTree::BronchExec(char const*, char const*, void*, bool, int, int) () from $ROOTSYS/lib/libTree.so.5.34
#12 0x00007f57e3e0eac8 in TTree::Bronch(char const*, char const*, void*, int, int) () from $ROOTSYS/lib/libTree.so.5.34
#13 0x00007f57e7b22ed3 in Branch (splitlevel=99, bufsize=8000, addobj=0xa57e5e8, classname=<optimized out>, name=0x7fff4dce1650 “GoodTrackSim.”, this=0xa5c72b0) at $ROOTSYS/include/TTree.h:326
Question is, is there a chance that this construction with holding the final class will work or rather I must redesign the class hierarchy?
The example above works, when the object in TClonesArray
is of BaseTrack
, but fails already for BaseTrackSim
and GoodTrack
, so I guess it is due to virtual inheritance.
I prepared MWE to demonstrate this. Please copy code below to shell script and execute, it will prepare all sources, compile, you just need to run ./a.out
. You can play in the constructor of TestClass
and change object to BaseTrack
and it will work.
#!/bin/bash
cat <<EOF > test_tree.h
#ifndef TESTTREE
#define TESTTREE
#include "TLorentzVector.h"
#include "TClonesArray.h"
#include "TTree.h"
#include <iostream>
using namespace std;
class BaseTrack : public TLorentzVector
{
public:
BaseTrack() { cout << "BaseTrack" << endl; }
ClassDef(BaseTrack,1); void print() {}
};
class BaseTrackSim : public virtual BaseTrack
{
public:
BaseTrackSim() { cout << "BaseTrackSim" << endl; }
ClassDef(BaseTrackSim,1); void print() {}
};
class GoodTrack: public virtual BaseTrack
{
public:
GoodTrack() { cout << "GoodTrack" << endl; }
ClassDef(GoodTrack,1); void print() {}
};
class GoodTrackSim: public GoodTrack, public BaseTrackSim
{
public:
GoodTrackSim() { cout << "GoodTrackSim" << endl; }
ClassDef(GoodTrackSim,1); void print() {}
};
class TestClass : public TObject
{
public:
TClonesArray * data;
TestClass() : TObject() {
data = new TClonesArray("GoodTrackSim", 10);
if (data)
{
for (int i = 0; i < 4; ++i)
{
std::cout << i << std::endl;
new (&((*data)[i])) GoodTrackSim;
}
}
else
{
std::cout << "No data\n";
}
}
ClassDef(TestClass,1);
};
#endif
EOF
cat <<EOF > test_tree.cpp
#include "test_tree.h"
#include "TFile.h"
ClassImp(BaseTrack);
ClassImp(BaseTrackSim);
ClassImp(GoodTrack);
ClassImp(GoodTrackSim);
ClassImp(TestClass);
int main()
{
TFile * f = TFile::Open("test.root", "RECREATE");
TTree * t = new TTree("T", "Tree");
TestClass * tc = new TestClass;
t->Branch("tc", &tc);
f->Write();
f->Close();
return 0;
}
EOF
cat <<EOF > LinkDef.h
#ifdef __CINT__
#pragma link off all globals;
#pragma link off all classes;
#pragma link off all functions;
#pragma link off all enum;
#pragma link C++ nestedclass;
//-------------------------------------
// enums
#pragma link C++ namespace TestClass;
#pragma link C++ namespace BaseTrack;
#pragma link C++ namespace BaseTrackSim;
#pragma link C++ namespace GoodTrack;
#pragma link C++ namespace GoodTrackSim;
#endif
EOF
cat <<EOF > quick_run.sh
#!/bin/bash
rootcint -f Dict.cpp -c -p test_tree.h LinkDef.h
g++ test_tree.cpp Dict.cpp $(root-config --glibs --cflags)
./a.out
EOF
chmod +x quick_run.sh
./quick_run.sh
echo
echo
echo
echo "Created 'test_tree.h', 'test_tree.cpp' and 'LinkDef.h'"
echo "You can edit them and run './quick_run.sh' to compile and execute the example"