Branches with custom classes

Dear experts,

I’m trying to build a TTree containing branches of a custom class. The tricky part is that such a custom class contains a vector of pointers to yet another custom class objects.

Class “HistoWrapper” is a container for TH1 and other standard variables like ints, strings etc.
Class “Topology” contains vector<HistoWrapper*>* and other standard variables.

When trying to build the shared library is complains:

.TopologyDict.cc:182: error: type ‘class std::vector<HistoWrapper*, std::allocator<HistoWrapper*> >’ argument given to ‘delete’, expected pointer

.TopologyDict.cc is autogenerated.

Here’s my linkdef.h file

#include "Topology.h"
#include "HistoWrapper.h"
#include <vector>
#ifdef __CINT__
#pragma link off all globals;
#pragma link off all classes;
#pragma link off all structs;
#pragma link off all functions;
#pragma link C++ class Topology;
#pragma link C++ class HistoWrapper;
#pragma link C++ class std::vector<HistoWrapper*>;
#endif

And the rest of the code can be found under /afs/cern.ch/user/n/nvallsve/public/nidra/

Any help will be much appreciated,

– Nil

Hi Nil,

The old streaming infrastructure does not handle well stl collection.
Try the new (StreamInfo based) streaming infrastructure by updating your linkdef file: #pragma link C++ class Topology+; #pragma link C++ class HistoWrapper+; #pragma link C++ class std::vector<HistoWrapper*>+;
You may also want to tell the I/O where the vector owns the pointer or not: #pragma read sourceClass="Topology" target="memberName" attributes="Owner";
Cheers,
Philippe.

Thanks a lot, Phillipe.

I’ve added the “+” at the end of the “#pragma link” directives. Now the compilation error is slightly different:

.HistoWrapper.o: In function `__static_initialization_and_destruction_0': /afs/cern.ch/user/n/nvallsve/CMSSW_4_2_3_patch2/src/HighMassAnalysis/Analysis/nidra/HistoWrapper.cc:462: undefined reference to `ROOT::GenerateInitInstance(HistoWrapper const*)' .HistoWrapper.o: In function `HistoWrapper::IsA() const': /afs/cern.ch/user/n/nvallsve/CMSSW_4_2_3_patch2/src/HighMassAnalysis/Analysis/nidra/HistoWrapper.h:140: undefined reference to `HistoWrapper::Class()' .HistoWrapper.o:(.data.rel.ro._ZTV12HistoWrapper[vtable for HistoWrapper]+0x1d0): undefined reference to `HistoWrapper::ShowMembers(TMemberInspector&)' .HistoWrapper.o:(.data.rel.ro._ZTV12HistoWrapper[vtable for HistoWrapper]+0x1d8): undefined reference to `HistoWrapper::Streamer(TBuffer&)' .RootFileMaker.o: In function `TBranch* TTree::Branch<Topology>(char const*, char const*, Topology**, int, int)': RootFileMaker.cc:(.text._ZN5TTree6BranchI8TopologyEEP7TBranchPKcS5_PPT_ii[TBranch* TTree::Branch<Topology>(char const*, char const*, Topology**, int, int)]+0x34): undefined reference to `typeinfo for Topology' .Topology.o: In function `__static_initialization_and_destruction_0': /afs/cern.ch/user/n/nvallsve/CMSSW_4_2_3_patch2/src/HighMassAnalysis/Analysis/nidra/Topology.cc:147: undefined reference to `ROOT::GenerateInitInstance(Topology const*)' .Topology.o: In function `Topology': /afs/cern.ch/user/n/nvallsve/CMSSW_4_2_3_patch2/src/HighMassAnalysis/Analysis/nidra/Topology.cc:10: undefined reference to `vtable for Topology' /afs/cern.ch/user/n/nvallsve/CMSSW_4_2_3_patch2/src/HighMassAnalysis/Analysis/nidra/Topology.cc:10: undefined reference to `vtable for Topology' collect2: ld returned 1 exit status make: *** [.nidra.exe] Error 1

I’m puzzled about the additional line you suggested to add:

I presume “Owner” should be kept like that, correct? On the other hand, I’m clueless about what “memberName” should be.

Thanks,

– Nil

Hi Nil,

‘memberName’ should be replaced by the actual name of the data member of the class Topology which is of type ‘std::vector<HistoWrapper*, std::allocator<HistoWrapper*> >’.

The error you see are link error and it seems that you may have not compiled or linked in the actual code for your classes.

Cheers,
Philippe.

Does the order of “#pragma link C++ class …” lines matter?
If yes, as the “Topology” uses “std::vector<HistoWrapper*>” which then uses “HistoWrapper”, maybe the linkdef file should contain: #pragma link C++ class HistoWrapper+; #pragma link C++ class std::vector<HistoWrapper*>+; #pragma link C++ class Topology+;

Hi all,

I managed to get it working. Indeed the last error had to do with a silly mistake including the shared libraries in the Makefile.

Pepe, I have changed around the order of the pragma directives without effect: as long as they are there the order doesn’t matter.

Thank you very much for your help,

– Nil

Hi again,

I was able to successfully write out custom class branches. The problem now appears when reading in those very branches. Suppose I’m writing/reading a branch of type “ClassB”:

ClassA(){
  int i =10;
}

ClassB(){
  one = new ClassA();
  two = new ClassA();
}

My linkdefs.h…

#include "ClassA.h"
#include "ClassB.h"

#ifdef __CINT__

#pragma link off all globals;
#pragma link off all classes;
#pragma link off all structs;
#pragma link off all functions;

#pragma link C++ class ClassA+;
#pragma link C++ class ClassB+;

#pragma read source="one" sourceClass="ClassB" target="one" targetClass="ClassB" attributes="Owner";
#pragma read source="two" sourceClass="ClassB" target="one" targetClass="ClassB" attributes="Owner";

The curious part is that if only a single ClassA pointer is present in ClassB, there is no problem.

ClassB(){
  one = new ClassA();
}

But this will case a segfault when calling the TTree::GetEntry()…

ClassB(){
  one = new ClassA();
  two = new ClassA();
}

Thanks,

– Nil

I now got it to work on a small example of the above. There must have been some other problem in the rest of the code.

– Nil

Dear all,

Trying to read in a TTree containing a single branch of (custom) type DitauBranches, which I have just successfully written out to a TFile… This is done in the following function:

void Plotter::MakePlots(Topology* iTopology){
   TTree* eventTree = iTopology->GetGoodEventsForSignal();
   DitauBranches* event = NULL;
   eventTree->SetBranchAddress("goodEventsForSignal", &event);
   for(unsigned int e = 0; e < eventTree->GetEntries(); e++){
      eventTree->GetEntry(e);
   }
}

Now, this function is called several times, i.e.:

MakePlots(myTopology1);
MakePlots(myTopology2);
MakePlots(myTopology3);
MakePlots(myTopology4);
...

It runs find the first time around, but it segfaults at the “eventTree->GetEntry(e);” on the second call. The interesting part is that it always segfaults on the second call of MakePlots, no matter what the pointer being passed is. For example, the above will segfault during the call taking in “myTopology2”, but…

//MakePlots(myTopology1);
MakePlots(myTopology2);
MakePlots(myTopology3);
MakePlots(myTopology4);
...

will segfault during the call taking in “myTopology3”, also when “eventTree->GetEntry(e);”

I have tried to make sense of the stack trace. Something fishy appears to be there with the dictionary that I generated with rootcint, but it escapes my debugging skills.

The complete code can be found at /afs/cern.ch/user/n/nvallsve/public/nidraSegfaults/

Thanks very much,

– Nil

===========================================================
There was a crash.
This is the entire stack trace of all threads:
===========================================================
#0  0x000000355049a115 in waitpid () from /lib64/libc.so.6
#1  0x000000355043c481 in do_system () from /lib64/libc.so.6
#2  0x00002ba97355f222 in TUnixSystem::StackTrace() ()
   from /afs/cern.ch/cms/slc5_amd64_gcc434/cms/cmssw-patch/CMSSW_4_2_3_patch2/external/slc5_amd64_gcc434/lib/libCore.so
#3  0x00002ba97355fcdc in TUnixSystem::DispatchSignals(ESignals) ()
   from /afs/cern.ch/cms/slc5_amd64_gcc434/cms/cmssw-patch/CMSSW_4_2_3_patch2/external/slc5_amd64_gcc434/lib/libCore.so
#4  <signal handler called>
#5  0x0000000000451594 in std::vector<float, std::allocator<float> >::clear (
    this=0xffffffff)
    at /afs/cern.ch/cms/slc5_amd64_gcc434/external/gcc/4.3.4-cms/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.3.4/../../../../include/c++/4.3.4/bits/stl_vector.h:892
#6  0x00002ba9732c3507 in ROOT::TCollectionProxyInfo::Type<std::vector<float, std::allocator<float> > >::clear (env=0x1d06cf90)
    at /afs/cern.ch/cms/slc5_amd64_gcc434/lcg/root/5.27.06b-cms16/include/TCollectionProxyInfo.h:140
#7  0x00002ba9744f72fe in TGenCollectionStreamer::Streamer(TBuffer&) ()
   from /afs/cern.ch/cms/slc5_amd64_gcc434/cms/cmssw-patch/CMSSW_4_2_3_patch2/external/slc5_amd64_gcc434/lib/libRIO.so
#8  0x00002ba9744d1be7 in TCollectionStreamer::Streamer(TBuffer&, void*, int, TClass*) ()
   from /afs/cern.ch/cms/slc5_amd64_gcc434/cms/cmssw-patch/CMSSW_4_2_3_patch2/external/slc5_amd64_gcc434/lib/libRIO.so
#9  0x00002ba973530ada in TClass::Streamer(void*, TBuffer&, TClass const*) const ()
   from /afs/cern.ch/cms/slc5_amd64_gcc434/cms/cmssw-patch/CMSSW_4_2_3_patch2/external/slc5_amd64_gcc434/lib/libCore.so
#10 0x00002ba974575128 in int TStreamerInfo::ReadBuffer<char**>(TBuffer&, char** const&, int, int, int, int) ()
   from /afs/cern.ch/cms/slc5_amd64_gcc434/cms/cmssw-patch/CMSSW_4_2_3_patch2/external/slc5_amd64_gcc434/lib/libRIO.so
#11 0x00002ba975040146 in TBranchElement::ReadLeavesMember(TBuffer&) ()
   from /afs/cern.ch/cms/slc5_amd64_gcc434/cms/cmssw-patch/CMSSW_4_2_3_patch2/external/slc5_amd64_gcc434/lib/libTree.so
#12 0x00002ba975030d95 in TBranch::GetEntry(long long, int) ()
   from /afs/cern.ch/cms/slc5_amd64_gcc434/cms/cmssw-patch/CMSSW_4_2_3_patch2/external/slc5_amd64_gcc434/lib/libTree.so
#13 0x00002ba97503cd57 in TBranchElement::GetEntry(long long, int) ()
   from /afs/cern.ch/cms/slc5_amd64_gcc434/cms/cmssw-patch/CMSSW_4_2_3_patch2/external/slc5_amd64_gcc434/lib/libTree.so
#14 0x00002ba97503ce6a in TBranchElement::GetEntry(long long, int) ()
   from /afs/cern.ch/cms/slc5_amd64_gcc434/cms/cmssw-patch/CMSSW_4_2_3_patch2/external/slc5_amd64_gcc434/lib/libTree.so
#15 0x00002ba975078180 in TTree::GetEntry(long long, int) ()
   from /afs/cern.ch/cms/slc5_amd64_gcc434/cms/cmssw-patch/CMSSW_4_2_3_patch2/external/slc5_amd64_gcc434/lib/libTree.so
#16 0x000000000045a257 in Plotter::MakePlots (this=0x7fff7668dba0, 
    iTopology=0x19ba1740) at Plotter.cc:82
#17 0x0000000000458cad in Plotter::MakePlots (this=0x7fff7668dba0, 
    iTopologies=0x18032e30) at Plotter.cc:107
#18 0x000000000045a42d in Plotter::MakePlots (this=0x7fff7668dba0, 
    iTopologies=0x16c14fa0) at Plotter.cc:55
#19 0x000000000045a799 in Plotter (this=0x7fff7668dba0, iParams=0x4759c0)
    at Plotter.cc:26
#20 0x0000000000436203 in PreparePlots () at Driver.cc:101
#21 0x00000000004394a2 in main (argc=3, argv=0x7fff7668dd48) at Nidra.cc:19
===========================================================


The lines below might hint at the cause of the crash.
If they do not help you then please submit a bug report at
http://root.cern.ch/bugs. Please post the ENTIRE stack trace
from above as an attachment in addition to anything else
that might help us fixing this issue.
===========================================================
#5  0x0000000000451594 in std::vector<float, std::allocator<float> >::clear (
    this=0xffffffff)
    at /afs/cern.ch/cms/slc5_amd64_gcc434/external/gcc/4.3.4-cms/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.3.4/../../../../include/c++/4.3.4/bits/stl_vector.h:892
#6  0x00002ba9732c3507 in ROOT::TCollectionProxyInfo::Type<std::vector<float, std::allocator<float> > >::clear (env=0x1d06cf90)
    at /afs/cern.ch/cms/slc5_amd64_gcc434/lcg/root/5.27.06b-cms16/include/TCollectionProxyInfo.h:140
#7  0x00002ba9744f72fe in TGenCollectionStreamer::Streamer(TBuffer&) ()
   from /afs/cern.ch/cms/slc5_amd64_gcc434/cms/cmssw-patch/CMSSW_4_2_3_patch2/external/slc5_amd64_gcc434/lib/libRIO.so
#8  0x00002ba9744d1be7 in TCollectionStreamer::Streamer(TBuffer&, void*, int, TClass*) ()
   from /afs/cern.ch/cms/slc5_amd64_gcc434/cms/cmssw-patch/CMSSW_4_2_3_patch2/external/slc5_amd64_gcc434/lib/libRIO.so
#9  0x00002ba973530ada in TClass::Streamer(void*, TBuffer&, TClass const*) const ()
   from /afs/cern.ch/cms/slc5_amd64_gcc434/cms/cmssw-patch/CMSSW_4_2_3_patch2/external/slc5_amd64_gcc434/lib/libCore.so
#10 0x00002ba974575128 in int TStreamerInfo::ReadBuffer<char**>(TBuffer&, char** const&, int, int, int, int) ()
   from /afs/cern.ch/cms/slc5_amd64_gcc434/cms/cmssw-patch/CMSSW_4_2_3_patch2/external/slc5_amd64_gcc434/lib/libRIO.so
#11 0x00002ba975040146 in TBranchElement::ReadLeavesMember(TBuffer&) ()
   from /afs/cern.ch/cms/slc5_amd64_gcc434/cms/cmssw-patch/CMSSW_4_2_3_patch2/external/slc5_amd64_gcc434/lib/libTree.so
#12 0x00002ba975030d95 in TBranch::GetEntry(long long, int) ()
   from /afs/cern.ch/cms/slc5_amd64_gcc434/cms/cmssw-patch/CMSSW_4_2_3_patch2/external/slc5_amd64_gcc434/lib/libTree.so
#13 0x00002ba97503cd57 in TBranchElement::GetEntry(long long, int) ()
   from /afs/cern.ch/cms/slc5_amd64_gcc434/cms/cmssw-patch/CMSSW_4_2_3_patch2/external/slc5_amd64_gcc434/lib/libTree.so
#14 0x00002ba97503ce6a in TBranchElement::GetEntry(long long, int) ()
   from /afs/cern.ch/cms/slc5_amd64_gcc434/cms/cmssw-patch/CMSSW_4_2_3_patch2/external/slc5_amd64_gcc434/lib/libTree.so
#15 0x00002ba975078180 in TTree::GetEntry(long long, int) ()
   from /afs/cern.ch/cms/slc5_amd64_gcc434/cms/cmssw-patch/CMSSW_4_2_3_patch2/external/slc5_amd64_gcc434/lib/libTree.so
#16 0x000000000045a257 in Plotter::MakePlots (this=0x7fff7668dba0, 
    iTopology=0x19ba1740) at Plotter.cc:82
#17 0x0000000000458cad in Plotter::MakePlots (this=0x7fff7668dba0, 
    iTopologies=0x18032e30) at Plotter.cc:107
#18 0x000000000045a42d in Plotter::MakePlots (this=0x7fff7668dba0, 
    iTopologies=0x16c14fa0) at Plotter.cc:55
#19 0x000000000045a799 in Plotter (this=0x7fff7668dba0, iParams=0x4759c0)
    at Plotter.cc:26
#20 0x0000000000436203 in PreparePlots () at Driver.cc:101
#21 0x00000000004394a2 in main (argc=3, argv=0x7fff7668dd48) at Nidra.cc:19
===========================================================

Hi Nil,

Try:void Plotter::MakePlots(Topology* iTopology){ TTree* eventTree = iTopology->GetGoodEventsForSignal(); DitauBranches* event = NULL; eventTree->SetBranchAddress("goodEventsForSignal", &event); for(unsigned int e = 0; e < eventTree->GetEntries(); e++){ eventTree->GetEntry(e); } eventTree->ResetBranchAddresses(); }so that the TTree does not remember the address of a (long gone) stack variable. If this does not solve the problem, try running the example with valgrind.

Cheers,
Philippe.